Copyright: Copyright © 1995 Rogue Wave Software, Inc. Alle Rechte vorbehalten.
Citation: Rogue Wave Standard C++ Bibliothek Benutzerhandbuch und Tutorial


Verwenden der Standardbibliothek mit BCC32
Verwenden der Standardbibliothek mit Borland BCC32 Compiler

Die Dokumentation zur Standard-Template-Bibliothek beschreibt eine Implementierung der Standardbibliothek, die mit dem ANSI/ISO C++ Arbeitspapier konsistent ist. Um eine vollkommen flexible Bibliothek zur Verfügung zu stellen, definiert das Arbeitspapier die Verwendung zweier Template-Funktionen, die in der aktuellen Version von BCC32.EXE, dem 32-Bit C++-Compiler von Borland noch nicht unterstützt werden. Bei diesen noch nicht unterstützten Funktionen handelt es sich um:

Elementfunktion-Templates

Verwendung von Template-Parametern zur Definition von Standardtypen

Obwohl die Dokumentation Informationen zu Funktionen der Standardbibliothek enthält, die nicht unterstützt werden, müssen Sie vor Beginn der Arbeit mit der Bibliothek keine besonderen Vorkehrungen treffen. Die Header-Datei definiert für jeden Container Alternativformen, die BCC32 automatisch in Ihren Quelltext einfügt. Sie müssen die erforderlichen Header-Dateien wie in diesem Hilfethema beschrieben einbinden.

Elementfunktion-Templates

Elementfunktion-Templates werden in allen Containern verwendet, die die Standardbibliothek zur Verfügung stellt. Ein Beispiel ist der Konstruktor für deque<T>, der zwei Iterator-Templates erwartet:

template <class InputIterator>

 deque (InputIterator, InputIterator);

deque besitzt auch eine Einfügefunktion dieses Typs. BCC32  unterstützt nicht die Verwendung von Funktionen, bei denen als Argumente beliebige Typen von Eingabeiteratoren verwendet werden. Die Header-Datei für jeden Container stellt Ersatzfunktionen zur Verfügung, mit denen Sie einen Iterator verwenden können, der aus dem gleichen Typ von Container stammt wie der, den Sie konstruieren (oder für den Sie eine Elementfunktion aufrufen). Sie können auch einen Zeiger auf den Elementtyp verwenden, der sich im Container befindet.

Um Elementfunktion-Templates zu vermeiden, können Sie zum Beispiel ein deque auf die folgenden zwei Arten konstruieren:

int intarray[10];

 deque<int> first_deque(intarray,intarray + 10);

deque<int>

 second_deque(first_deque.begin(),first_deque.end());

So können Sie hingegen ein deque auf diese Art nicht konstruieren:

deque<long>

 long_deque(first_deque.begin(),first_deque.end());

Der Grund dafür ist, daß long_deque und first_deque nicht vom gleichen Typ sind.

Ein Container kann außer dem Konstruktor weitere Elementfunktion-Templates besitzen. Im Allgemeinen stellt die Header-Datei für jeden Container eine alternative Prototypfunktion zur Verfügung, die kein Template ist.

Template-Parameter

Ein Funktions-Template kann Template-Parameter verwenden, die mit einem voreingestellten Wert initialisiert werden. Die folgenden Themen beschreiben den Umfang der BCC32 Unterstützung und wie Sie die Standardbibliothek verwenden sollten.

Voreingestellte Template-Argumente

BCC32 unterstützt die folgende Form voreingestellter Template-Argumente:

template < class T = int > class Array;

Diese Syntax unterstützt die Konstruktion von Array-Objekten, die per Voreinstellung Container für int-Typen sind. Anstelle von int kann auch jeder beliebige andere Typ verwendet werden (einschließlich anderer benutzerdefinierter Typen).

Verwendung von Template-Parametern zur Definition voreingestellter Typen

BCC32 unterstützt keine Funktionen mit Template-Parametern, die zur Angabe von voreingestellten Typen verwendet werden. Aus diesem Grund müssen Sie immer alle Template-Argumente zur Verfügung stellen, die andernfalls einen ihrer Parameter zur Generierung eines voreingestellten Typs verwenden würden.

Es existiert beispielsweise eine Version des Containers stack, die einen Template-Parameter zur Definition eines voreingestellten Typs für einen anderen Parameter verwendet. In der folgenden Deklaration wird der generische Typ T verwendet, um ein deque-Objekt zu instantiieren. deque jedoch ist ein generischer Typ, der von einem generischen Typ T abhängt.

Die Deklaration geschieht folgendermaßen:

template <class T, class Container = deque<T> >

 class stack;  // This form is not supported

Die Header-Datei STACK.H stellt eine alternative Version zur Verfügung, die von BCC32 unterstützt wird. Diese Klassendeklaration erweitert nicht die Fähigkeit von Template-Parametern, andere Parameter zu definieren. Die Deklaration geschieht folgendermaßen:

template <class T, class Container> class stack;

Um einen Typ stack zu konstruieren, müssen Sie immer alle Argumente zur Verfügung stellen. Sie müssen Ihren Typ stack instantiieren, indem Sie beispielsweise folgendermaßen vorgehen:

stack<double, deque<double> > MyStack;

Verwenden der Header-Dateien der Standardbibliothek

Damit die Implementation der Standardbibliothek einwandfrei arbeitet, müssen Sie Dateien immer wie in diesem Dokument beschrieben einbinden. Um beispielsweise die Implementierung von string der Standardbibliothek zu verwenden, binden Sie die die folgende Zeile in ihren Quelltext ein:

#include <string>

Entsprechend binden Sie die die folgende Zeile in ihren Quelltext ein, wenn Sie die generischen Algorithmen der Standardbibliothek verwenden möchten:

#include <algorithm>


Rogue Wave: C++ Standardbibliothek
Rogue Wave

C++ Standardbibliothek
Benutzerhandbuch,
Tutorial und
Klassenreferenz

         Rogue Wave Software

         Corvallis, Oregon USA


Die beiliegende Software, einschließlich eines oder mehrerer der folgenden Bestandteile, jedoch nicht auf diese beschränkt: Quelltext, Objekt-Code, dynamische Link-Bibliotheken, gemeinsam verwendete Bibliotheken, statische Bibliotheken, Header-Dateien, Hilfsprogramme und Scripts sowie die zugehörige Dokumentation (zusammen die „Software“ genannt), ist Eigentum von Rogue Wave oder seinen Zulieferern und unterliegt dem Schutz der US-Copyright-Gesetze und internationaler Vereinbarungen. Sie ist für die Verwendung durch einen C++ Programmierer gedacht, der Erfahrung im Umgang mit C++ Klassenbibliotheken besitzt. Die Software ist nicht für den Einsatz durch Endanwender für den Hausgebrauch vorgesehen.

Rogue Wave gewährt Ihnen (einem Software-Programmierer) das eingeschränkte Recht zur Benutzung nur eines Exemplars (Kopie) der Software auf einem einzelnen Terminal, das mit einem einzelnen Computer (im Normalfall einer Workstation) verbunden ist, auf der Grundlage der in dieser Vereinbarung dargelegten Bestimmungen und Bedingungen. Jeder Software-Programmierer muß eine eigene Lizenz besitzen, um die Software verwenden zu dürfen. Sie sind berechtigt, (a) eine Sicherungskopie der Software einzig zum Zweck der Datensicherung anzufertigen oder (b) die Software auf eine Festplatte zu übertragen und die Originalversion einzig zum Zweck der Datensicherung zu verwenden.

Unter den in dieser Vereinbarung genannten Beschränkungen sind Sie berechtigt, die dynamischen Link-Bibliotheken, die statisch gelinkten Bibliotheken sowie die gemeinsam verwendeten Bibliotheken in Software-Anwendungsprodukte, die Sie entwickeln, einzubinden. Ferner ist es Ihnen gestattet, die dynamischen Link-Bibliotheken, die statisch gelinkten Bibliotheken sowie die gemeinsam verwendeten Bibliotheken zu verändern und diese bearbeiteten dynamischen Link-Bibliotheken, statisch gelinkten Bibliotheken sowie gemeinsam verwendeten Bibliotheken in Software-Anwendungsprodukte, die Sie entwickeln, einzubinden. Sie dürfen Kopien der dynamischen Link-Bibliotheken, der statisch gelinkten Bibliotheken sowie der gemeinsam verwendeten Bibliotheken der Software, so wie sie in den C++ Software-Anwendungsprodukten, die Sie entwickeln, eingebunden sind, anfertigen und vertreiben, unter der Voraussetzung, daß die Software oder andere Produkte von Rogue Wave keinen Hauptanteil am Wert Ihres Produkts darstellen.

Unberührt von jeglichen gegenteiligen Bestimmungen in dieser Vereinbarung ist es Ihnen NICHT gestattet, (a) in irgendeiner Weise eine der Header-Dateien, den Quelltext, Objektmodule, auf Templates basierende Klassen oder unabhängige statische Bibliotheken der Software weiterzugeben; oder (b) irgendeinen Teil der Software oder eine irgendwie geartetete Ableitung irgendeines Teils der Software in einem Software-Hilfsprogramm oder in anderer in Konkurrenz zu Rogue Waves Vertrieb der Software stehender Weise weiterzugeben. Sie dürfen Quelltext oder Objekt-Code der Software - weder ganz noch teilweise - nicht in anderer Weise, als ausdrücklich durch diese Vereinbarung eingeräumt, verwenden, kopieren, verändern, zusammenführen oder compilieren. Es ist Ihnen NICHT gestattet, (a) jeglichen Objektcode beliebiger Teile der Software zu decompilieren, disassemblieren oder ein umgekehrtes Engineering durchzuführen;.oder (b) ohne vorherige schriftliche Zustimmung von Rogue Wave und alle benötigten Exportlizenzen und Regierungsgenehmigungen aus den Vereinigten Staaten zu exportieren; oder (c) die Software zu vermieten oder zu verleasen; oder (d) jeglichen Quelltext der Software Personen oder Körperschaften offenzulegen; oder (e) die Dokumentation zu kopieren. Die Quelltexte der Software stellen für Rogue Wave Vermögenswerte dar. Sie verpflichten sich, alle Quelltexte der Software vertraulich zu behandeln.

Sie dürfen die Software oder die Ihnen in dieser Vereinbarung eingeräumten Rechte nicht übertragen oder übereignen.

ROGUE WAVE IST UNTER KEINEN UMSTÄNDEN FÜR JEGLICHE INDIREKTE, BESONDERE, ZUFÄLLIGE SCHÄDEN ODER FOLGESCHÄDEN (EINSCHLIESSLICH VERMÖGENSFOLGESCHÄDEN) HAFTBAR, GLEICHGÜLTIG, OB SIE AUF VERTRÄGEN, UNERLAUBTEN HANDLUNGEN ODER ANDEREN JURISTISCHEN SACHVERHALTEN BERUHEN. DIES GILT SELBST DANN, WENN ROGUE WAVE ÜBER DIE MÖGLICHKEIT SOLCHER SCHÄDEN INFORMIERT WURDE.

EINGESCHRÄNKTES NUTZUNGSRECHT DURCH DIE US-REGIERUNG

Die SOFTWARE sowie die Dokumentation werden mit EINGESCHRÄNKTEM NUTZUNGSRECHT zur Verfügung gestellt. Verwendung, Duplizierung oder Offenlegung durch die Regierung unterliegt den in den Unterparagraphen (c)(1)(ii) der Verordnung Rights in Technical Data and Computer Software in DFARS 252.227-7013 oder Unterparagraphen (c)(1) und (2) der Commercial Computer Software--Restricted Rights in 48 CFR 52.227-19 dargelegten Einschränkungen, soweit zutreffend. Vertragsgeber/Hersteller ist Rogue Wave Software, Inc., P.O. Box 2328, Corvallis, Oregon  97339.

Diese Vereinbarung unterliegt den Gesetzen des Staates Oregon.


Rogue Wave C++ Standardbibliothek, Benutzerhandbuch und Tutorial

für

Rogue Waves Implementation der C++ Standardbibliothek.

Basierend auf dem ANSI-Arbeitspapier Working Paper for Draft Proposed International Standard for Information Systems--Programming Language C++.   28. April 1995.

Autor Benutzerhandbuch und Tutorial:	Timothy A. Budd

Autoren der Klassenreferenz:  	Wendi Minne, Tom Pearson und Randy Smithey

Produkt-Team:

Entwicklung:	Anna Dahan, Donald Fowler, Mike Lijewski, Randy Smithey

Qualitätssicherung:  	Dale Oberg, Randall Robinson, Chun Zhang

Handbücher:  	Mike Lijewski, Wendi Minne, Kristi Moore, Randy Smithey

Support: 	North Krimsley

Wesentliche Beiträge von:	Josh Gray , Rodney Mishima

Copyright © 1995 Rogue Wave Software, Inc. Alle Rechte vorbehalten.


Einführung

1.1 Was ist die C++ Standardbibliothek?

1.2 Was unterscheidet die C++ Standardbibliothek von anderen Bibliotheken?

1.3 Wie wirkt sich das nicht objektorientierte Konzept aus?

1.4 Wie wird die C++ Standardbibliothek verwendet?

1.5 Umgang mit diesem Handbuch

1.6 Konventionen

1.7 Gebrauch der Standardbibliothek

1.8 Ausführen der Beispielprogramme


Was ist die C++ Standardbibliothek?

Die International Standards Organization (ISO) und das American National Standards Institute (ANSI) stehen kurz vor dem Abschluß des Normierungsverfahrens für die Programmiersprache C++. Ein Hauptergebnis dieses Normierungsverfahrens ist die "C++ Standardbibliothek", eine umfangreiche und umfassende Sammlung von Klassen und Funktionen. Das vorliegende Produkt ist die Implementierung der Standardbibliothek nach ANSI/ISO-Norm durch Rogue Wave.

Die C++ Standardbibliothek nach ANSI/ISO-Norm besteht aus den folgenden Teilen:

Eine große Sammlung von Datenstrukturen und Algorithmen, die bisher unter dem Namen Standard Template Library (STL) bekannt war.

Eine IOStream-Funktion.

Eine Lokale-Funktion.

Ein string-Klassen-Template.

Ein Klassen-Template für die Darstellung komplexer Zahlen.

Ein einheitliches Framework für die Beschreibung der Ausführungsumgebung, unter Verwendung eines Klassen-Templates mit dem Namen numeric_limits und mittels Spezialisierung für jeden grundlegenden Datentyp.

Funktionen zur Speicherverwaltung.

Funktionen zur Sprachunterstützung.

Funktionen für die Exception-Behandlung.

Diese Version der Rogue Wave C++ Standardbibliothek enthält die Bibliotheken der Datenstrukturen und Algorithmen (STL) sowie die Klassen string, complex und numeric_limits.


Was unterscheidet die C++ Standardbibliothek von anderen Bibliotheken?

Ein Hauptteil der C++ Standardbibliothek besteht aus einer Sammlung von Klassendefinitionen für Standard-Datenstrukturen und einer Sammlung von Algorithmen, die ganz allgemein für die Bearbeitungen solcher Strukturen verwendet werden. Dieser Teil der Bibliothek war bisher unter dem Namen Standard Template Library oder STL bekannt. Organisation und Konzept der STL unterscheiden sich in nahezu jeder Hinsicht vom Konzept der meisten anderen C++ Bibliotheken, da sie Kapselung vermeidet und kaum Gebrauch von Vererbung macht.

Die Betonung der Kapselung ist ein Schlüsselmerkmal der objektorientierten Programmierung. Das Zusammenbringen von Daten und Funktionen in einem Objekt ist ein leistungsfähiges Organisationsprinzip in der Software-Entwicklung, es stellt sogar das wichtigste Organisationsprinzip dar. Durch den sinnvollen Einsatz von Kapselung können selbst außerordentlich komplexe Software-Systeme in handhabbare Einheiten unterteilt und verschiedenen Mitgliedern eines Programmiererteams zur Entwicklung zugeteilt werden.

Vererbung ist eine leistungsfähige Technik, wenn es um die gemeinsame Verwendung von Quelltext und um Wiederverwendung von Software geht, aber sie ist vor allem dann mit Erfolg einzusetzen, wenn zwei oder mehr Klassen eine Reihe von Grundzügen gemeinsam haben. In einer grafischen Benutzeroberfläche können beispielsweise zwei Fensterarten aus einer gemeinsamen Fenster-Basisklasse abgeleitet sein, und die einzelnen Unterklassen stellen dann alle erforderlichen Merkmale zur Verfügung, die für ihre enger definierte Aufgabe erforderlich sind. Ein anderes Einsatzgebiet für Vererbung könnten objektorientierte Container-Klassen darstellen, die gleichartiges Verhalten und die Wiederverwendung von Quelltext durch die Ableitung aus einer allgemeineren Klasse sicherstellen, und die allgemeine Elementfunktionen einsetzen.

Die Entwickler der STL haben sich gegen einen völlig objektorientierten Ansatz entschieden und haben die Aufgaben, die unter Verwendung allgemeiner Datenstrukturen ausgeführt werden, von der Repräsentation der Strukturen selbst getrennt. Aus diesem Grund wird die STL korrekt als eine Sammlung von Algorithmen und getrennt von diesen als eine Sammlung von Datenstrukturen angesehen, die mit Hilfe der Algorithmen bearbeitet werden können.


Wie wirkt sich das nicht objektorientierte Konzept aus?

Der STL-Teil der C++ Standardbibliothek wurde absichtlich in einer nicht objektorientierten Architektur konzipiert. Dieses Konzept hat eine Reihe von Auswirkungen - einigen vorteilhaften, einigen weniger vorteilhaften - die Entwickler berücksichtigen müssen, wenn sie die Bibliothek möglichst effektiv verwenden möchten. Wir werden einige dieser Auswirkungen hier erläutern.

Schlankerer Quelltext

Es gibt ungefähr fünfzig verschiedene Algorithmen in der STL und etwa ein Dutzend grundlegende Datenstrukturen. Diese Trennung führt zu einer Verringerung der Größe des Quelltexts und vermindert zum Teil das Risiko, daß ähnliche Aktivitäten unterschiedliche Schnittstellen erhalten. Ohne diese Trennung müßte beispielsweise jeder der Algorithmen in jeder einzelnen der verschiedenen Datenstrukturen neu implementiert werden, und das würde einige Hundert Elementfunktionen mehr erfordern, als sich im aktuellen Konzept finden.

Flexibilität

Ein Vorteil der Trennung von Algorithmen und Datenstrukturen liegt darin, daß derartige Algorithmen in Verbindung mit konventionellen C++ Zeigern und Arrays verwendet werden können. Da C++ Arrays keine Objekte darstellen, verfügen in einer Klassenhierarchie gekapselte Algorithmen nur selten über diese Fähigkeit.

Effizienz

Die STL im besonderen und die C++ Standardbibliothek im allgemeinen stellen einen Ansatz auf unterer Ebene für die Entwicklung von C++ Anwendungen dar, sozusagen aus Grundelementen. Dieser Ansatz auf unterer Ebene kann hilfreich sein, wenn bei bestimmten Programmen die Codegröße und die Ausführungsgeschwindigkeit einen Schwerpunkt bilden.

Iteratoren: Nichtübereinstimmung und Ungültigkeit

Die Datenstrukturen der C++ Standardbibliothek verwenden zeigerähnliche Objekte, die Iteratoren genannt werden, um den Inhalt eines Containers zu beschreiben. (Sie werden ausführlich in Abschnitt 2 behandelt). Aufgrund der Struktur der Bibliothek besteht keine Möglichkeit zu prüfen, ob diese Iteratorelemente übereinstimmen, d.h. sicherzustellen, daß sie aus dem gleichen Container abgeleitet sind. Den Anfangsiterator aus einem Container zusammen mit dem Endeiterator aus einem anderen zu verwenden (sei es absichtlich oder zufällig) ist ein sicherer Weg ins Verderben.

Es ist äußerst wichtig zu beachten, daß Iteratoren als Folge eines späteren Einfüge- oder Löschvorgangs in der zugrundeliegenden Container-Klasse ungültig werden können. Dieses Ungültigwerden wird nicht überprüft, und die Verwendung eines ungültigen Iterators kann zu unerwarteten Ergebnissen führen.

Vertrautheit im Umgang mit der C++ Standardbibliothek hilft, die Zahl der auf Iteratoren zurückzuführenden Fehler gering zu halten.

Templates: Fehler und "aufgeblähter" Code

Die Flexibilität und Leistungsfähigkeit von Algorithmen-Templates wird bei den meisten Compilern mit einer weniger präzisen Diagnostik erkauft. Fehler in den Parameterlisten generischer Algorithmen zeigen sich manchmal nur als seltsame Compilerfehler für interne Funktionen, die viele Ebenen tief in den Template-Erweiterungen definiert sind. Auch hier bedeutet Vertrautheit mit den Algorithmen und ihren Erfordernissen den Schlüssel für den erfolgreichen Einsatz der Standardbibliothek.

Die STL baut in hohem Maße auf Templates auf. Das kann dazu führen, daß die Größe compilierter Programme das gewohnte Maß überschreitet. Dieses Problem kann verringert werden, wenn der Entwickler ein Gespür dafür entwickelt, welcher Preis für die Instanziierung einer bestimmten Template-Klasse zu entrichten ist, und die entsprechenden konzeptionellen Entscheidungen trifft. Es sollte aber auch bedacht werden, daß dieses Problem in Zukunft weniger bedeutend sein wird, da Compiler mit besseren Fähigkeiten zum Umgang mit Templates versehen sein werden.

Multithreading-Probleme

Die C++ Standardbibliothek muß in Multithreading-Umgebungen mit Vorsicht eingesetzt werden. Da Iteratoren unabhängig von den Containern existieren, auf die sie sich beziehen, können sie nicht auf sichere Weise zwischen einzelnen Threads übergeben werden. Da Iteratoren auch zur Bearbeitung von Containern, die nicht zum Typ const gehören, eingesetzt werden können, gibt es keine Möglichkeit, einen solchen Container zu schützen, wenn er Iteratoren in mehrere Threads absetzt. Verwenden Sie "thread-gesicherte" Rahmenobjekte, wie etwa die, die in Tools.h++ zur Verfügung stehen, wenn Sie auf einen Container von mehreren Threads aus zugreifen müssen.


Wie wird die C++ Standardbibliothek verwendet?

In einigen Jahren wird die C++ Standardbibliothek die Grundmenge der Klassen und Bibliotheken darstellen, die zu allen ANSI-konformen C++-Compilern mitgeliefert wird. Wir haben bereits darauf hingewiesen, daß das Konzept eines Großteils der C++-Standardbibliothek nicht objektorientiert ist. Andererseits zeichnet sich C++ gerade durch seine hervorragende Eignung zur Bearbeitung von Objekten aus. Wie läßt sich also die nicht objektorientierte Struktur der Standardbibliothek mit den Stärken von C++ in der Bearbeitung von Objekten zusammenführen?

Der entscheidende Punkt besteht in der Verwendung des richtigen Werkzeugs für jede Arbeit. Die objektorientierten Entwicklungsmethoden und Programmiertechniken sind nahezu ohne Konkurrenz, wenn es um die Orientierung in großen und komplexen Software-Projekten geht. Für die große Mehrzahl der Programmieraufgaben werden objektorientierte Techniken auch weiterhin das Mittel der Wahl darstellen. Und Produkte wie Tools.h++ 7.0 von Rogue Wave, die die C++ Standardbibliothek in einer vertrauten objektorientierten Schnittstelle kapseln, stellen dem Entwickler die Leistungsfähigkeit der Bibliothek und die Vorteile der Objektorientierung zugleich zur Verfügung.

Setzen Sie die Komponenten der C++ Standardbibliothek direkt ein, wenn es um Flexibilität und/oder hocheffizienten Programm-Code geht. Verwenden Sie die eher traditionellen Ansätze der objektorientierten Entwicklung, wie etwa Kapselung und Vererbung, wenn größere Problembereiche dargestellt werden müssen, und verbinden Sie dann alle Stücke zu einer umfassenden Gesamtlösung. Wenn Sie eine Architektur für Ihre Anwendung entwerfen, erwägen Sie in jedem Fall den Gebrauch von Kapselung und Vererbung, schon allein, um das Problem zu gliedern. Wenn Sie aber feststellen, daß Sie für ein scharf umrissenes Problem - wie etwa die Bearbeitung eines Daten-Streams in einem Treiber (die Art Aufgabe, die sich häufig auf den Einsatz einer einzigen Klasse zurückführen läßt) - eine hocheffiziente Datenstruktur oder einen Algorithmus benötigen, richten Sie Ihr Augenmerk auf die C++ Standardbibliothek. Die C++ Standardbibliothek entfaltet ihre Vorzüge in der Erzeugung wiederverwendbarer Klassen, wenn es um Konstrukte auf unterer Ebene geht, während die traditionellen OOP-Techniken dann glänzen, wenn solche Klassen zur Lösung eines größeren Problems zusammengeführt werden müssen.

In Zukunft werden die meisten Bibliotheken die C++ Standardbibliothek als Grundlage verwenden. Durch Einsatz der C++ Standardbibliothek - sei es direkt oder in Form einer Kapselung wie Tools.h++ 7.0 - tragen Sie zur Sicherstellung von Interoperabilität bei. Das ist vor allem in großen Projekten wichtig, die unter Umständen auf der Kommunikation zwischen verschiedenen Bibliotheken aufbauen. Es ist eine gute Faustregel, den höchsten verfügbaren Kapselungsgrad zu verwenden, aber stets sicherzustellen, daß die C++ Standardbibliothek als Basis für die Kommunikation und den Austausch zwischen den einzelnen Bibliotheken zur Verfügung steht.

Die Sprache C++ unterstützt eine große Bandbreite von Programmieransätzen, auch weil die zu lösenden Probleme diese Bandbreite erfordern. Die Sprache - und nun auch die C++ Standardbibliothek, die sie unterstützt - wurde so konzipiert, daß der Entwickler jedes einzelne Problem auf dem günstigsten Weg angehen kann. Die C++ Standardbibliothek stellt - in Kombination mit traditionelleren Techniken der objektorientierten Programmierung - ein sehr vielseitig einsetzbares Werkzeug für jeden dar, der eine Kollektion von C++ Klassen erzeugt, unabhängig davon, ob solche Klassen als Bibliothek für sich stehen sollen oder ob sie für eine bestimmte Aufgabe maßgeschneidert werden.


Umgang mit diesem Handbuch

Dieses Handbuch ist eine Einführung in die Implementation der C++-Standardbibliothek von Rogue Wave. Es wird davon ausgegangen, daß Sie bereits mit den Grundzügen der Programmiersprache C++ vertraut sind. Wenn C++ für Sie Neuland ist, sollten Sie zuerst eine allgemeine Einführung lesen, wie zum Beispiel das Buch "Die C++ Programmiersprache" von Bjarne Stroustrup (Addison-Wesley, 1992).

Mit dem Container-Klassen-Abschnitt der Standardbibliothek hängt ein typisches "Henne-Ei-Problem" eng zusammen. Das Kernstück der Container-Klassenbibliothek besteht in der Definition der Container selbst, aber die Nützlichkeit dieser Strukturen läßt sich ohne Verständnis der Algorithmen, die ihre Funktionalität außerordentlich erweitern, nicht wirklich erkennen. Auf der anderen Seite sind aber auch die Algorithmen nur wirklich zu verstehen, wenn bereits einige Kenntnis über die Container vorhanden ist.


Konventionen

Wir haben sowohl die Klassennamen als auch die Funktionsnamen () bei ihrem ersten Auftreten im Text in einer eigenen Schriftart gesetzt. Ferner werden wir dem Namen einer Funktion oder eines Algorithmus ein Paar leere, runde Klammern nachstellen, wenn wir uns auf eine Funktion oder auf einen Algorithmus beziehen möchten, ohne in diesem Zusammenhang Wert auf die Argumente zu legen. Dies trifft auch dann zu, wenn der reale Funktionsaufruf zusätzliche Argumente benötigt. Wir haben den Ausdruck Algorithmus als Bezeichnung für die Funktionen im Abschnitt generische Algorithmen der Standardbibliothek verwendet, um Verwechslungen mit Elementfunktionen, Argumentfunktionen und vom Programmierer definierten Funktionen zu vermeiden. Beachten Sie, daß sowohl Klassennamen als auch Funktionsnamen sich an der verbreiteten Konvention orientieren, ein Unterstrich-Zeichen als Trennzeichen zu verwenden. Im gesamten Text erscheinen die Namen von Beispielen und Dateien in der Schriftart Courier, die auch für Funktionsnamen verwendet wird.

Im Text folgen wir der Gewohnheit, den Klassennamen nicht mehr in einer anderen Schriftart zu schreiben, nachdem er einmal eingeführt wurde. Dies geschieht in der Absicht, den Textfluß optisch nicht unnötig zu stören. Wir kehren jedoch dann zu einer besonderen Schriftart zurück, wenn eine Unterscheidung zwischen verschiedenen Möglichkeiten getroffen werden muß, wie zum Beispiel zwischen den Klassen Vector und List, die als Container für die Konstruktion eines Stacks verwendet werden.


Gebrauch der Standardbibliothek

Da die C++ Standardbibliothek in der Hauptsache aus Template-Deklarationen besteht, ist es auf den meisten Plattformen lediglich erforderlich, die entsprechenden Header-Dateien in Ihr Programm aufzunehmen. Diese Header-Dateien sind jeweils in dem Beschreibungstext für jeden Algorithmus und jede Klasse vermerkt.


Ausführen der Beispielprogramme

Alle Beispielprogramme, die in diesem Text beschrieben sind, wurden zusammengestellt und sind im Lieferumfang des Pakets enthalten. Sie können diese Programme compilieren und ausführen und sie als Modelle für eigene Programmierprobleme verwenden. Viele der Beispielprogramme wurden um zusätzliche Ausgabebefehle erweitert, die im Text nicht abgedruckt sind. Die erwartete Ausgabe jedes Programms wurde ebenfalls in das Distributionspaket aufgenommen.


Iteratoren

2.1 Einführung in Iteratoren

2.2 Arten von Iteratoren

2.3 Stream-Iteratoren

2.4 Einfügeiteratoren

2.5 Iteratoroperationen


Einführung in Iteratoren

Hinweis:	Iteratoren sind zeigerähnliche Objekte, die verwendet werden, um auf alle Objekte, die sich in einem Container befinden, der Reihe nach zuzugreifen.

Das Konzept des Iterators ist grundlegend für den Gebrauch der Container-Klassen und der mit ihnen verbundenen Algorithmen, die von der Standardbibliothek zur Verfügung gestellt werden. Abstrakt betrachtet ist ein Iterator einfach ein zeigerähnliches Objekt, das verwendet wird, um reihum auf alle Elemente, die in einem Container gespeichert sind, zuzugreifen. Da unterschiedliche Algorithmen Container auf mehrere verschiedene Arten durchlaufen müssen, gibt es verschiedene Arten von Iteratoren. Jede Container-Klasse in der Standardbibliothek kann einen Iterator mit der Funktionalität erzeugen, die der bei der Implementierung des Containers verwendeten Speichertechnik angemessen ist. In der Hauptsache bestimmt die Kategorie der als Argument erforderlichen Iteratoren, welche Algorithmen der Standardbibliothek zusammen mit welchen Container-Klassen verwendet werden können.

Hinweis:	Ein Wertebereich ist eine Folge von Werten in einem Container. Der Wertebereich wird durch ein Iteratorpaar bezeichnet, das Anfang und Ende der Sequenz definiert.

So wie Zeiger in der traditionellen Programmierung auf verschiedene Weise verwendet werden können, lassen sich auch Iteratoren für eine Reihe verschiedener Zwecke einsetzen. Ein Iterator kann verwendet werden, um einen bestimmten Wert zu bezeichnen, genauso wie ein Zeiger benutzt werden kann, um eine bestimmte Speicherstelle zu referenzieren. Andererseits kann ein Iteratorpaar eingesetzt werden, um einen Wertebereich von Werten zu bezeichnen, analog zu der Art, in der zwei Zeiger einen zusammenhängenden Speicherbereich angeben können. Im Fall der Iteratoren folgen aber die so bezeichneten Werte nicht mit Notwendigkeit auch physikalisch aufeinander; vielmehr ist die Reihenfolge eine logische, weil sie aus dem gleichen Container abgeleitet sind, und der zweite Wert auf den ersten in der Reihenfolge folgt, in der die Elemente vom Container gehalten werden.

Konventionelle Zeiger können manchmal null sein, das heißt, sie zeigen auf nichts. Auch Iteratoren bezeichnen unter Umständen keinerlei Wert. Ebenso, wie es ein logischer Fehler ist, einen Nullzeiger zu dereferenzieren, ist es ein Fehler, einen Iterator zu dereferenzieren, der keinen Wert bezeichnet.

Wenn in einem C++ Programm zwei Zeiger zum Bezeichnen eines Speicherbereichs verwendet werden, wird gemäß Konvention der Endezeiger nicht als Teil des bezeichneten Bereichs angesehen. Man kann z.B. von einem Array mit dem Namen x und einer Länge von 10 auch sagen, es erstrecke sich von x bis x+10, obwohl das Element an der Position x+10 nicht Bestandteil des Arrays ist. Vielmehr ist der Zeigerwert x+10 der Nach-Bereichsende-Wert - das Element, das den nächsten Wert nach dem Ende des bezeichneten Bereichs hat. Iteratoren werden zum Beschreiben von Bereichen auf gleiche Weise verwendet. Der zweite Wert wird nicht als Teil des bezeichneten Bereichs angesehen. Vielmehr ist der zweite Wert der Nach-Bereichsende-Wert, der den in der Folge nächsten Wert nach dem letzten Wert des Bereichs bezeichnet. Manchmal ist das, genau wie bei Zeigern auf Speicher, ein Wert, der sich tatsächlich im Container befindet. In anderen Fällen kann das ein Sonderwert sein, der nur für diesen Zweck konstruiert wird. In jedem Fall ist es inkorrekt, einen Iterator zu dereferenzieren, der für das Bezeichnen eines Bereichsendes verwendet wird.

Genau wie bei konventionellen Zeigern, ist die Basisoperation zum Bearbeiten von Iteratoren der Inkrementoperator (Operator ++). Wenn der Inkrementoperator auf einen Iterator angewendet wird, der den letzten Wert in einer Sequenz bezeichnet, nimmt dieser den "Nach-Bereichsende"-Wert an. Ein Iterator j wird als erreichbar für einen Iterator i angesehen, wenn der Iterator i nach einer endlichen Zahl von Anwendungen des Ausdrucks ++i gleich j ist.

Hinweis:	Wenn Iteratoren für die Bezeichnung eines Wertebereichs in einem Container verwendet werden, wird davon ausgegangen (aber nicht überprüft), daß der zweite Iterator von dem ersten aus erreichbar ist. Ist diese Bedingung nicht erfüllt, treten Fehler auf.

Wertebereiche können verwendet werden, um den gesamten Inhalt eines Containers zu bezeichnen, indem ein Iterator auf das erste Element und ein spezieller "Ende"iterator konstruiert werden. Bereiche können auch verwendet werden, um Teilsequenzen innerhalb eines einzelnen Containers zu bezeichnen, indem zwei Iteratoren auf bestimmte Werte eingesetzt werden. Immer, wenn ein Bereich mit zwei Iteratoren bezeichnet wird, wird davon ausgegangen, aber nicht geprüft, daß der zweite Iterator von dem ersten aus erreichbar ist. Ist das nicht der Fall, dann können Fehler auftreten.

In dem verbleibenden Teil dieses Abschnitts werden wir die verschiedenen Arten von Iteratoren, die in der Standardbibliothek verwendet werden, sowie verschiedene mit Iteratoren zusammenhängende Funktionen beschreiben.


Arten von Iteratoren

Fünf Grundformen von Iteratoren werden in der Standardbibliothek verwendet:

Eingabeiterator	Nur Lesezugriff, vorwärtsschreitend

Ausgabeiterator	Nur Schreibzugriff, vorwärtsschreitend

Vorwärtsiterator	Lese- und Schreibzugriff, vorwärtsschreitend

Bidirektionaler Iterator	Lese- und Schreibzugriff, vorwärts- und rückwärtsschreitend

Random Access-Iterator	Schreib- und Lesezugriff, direkter Zugriff

Iteratorkategorien sind hierarchisch. Vorwärtsiteratoren können an allen Stellen eingesetzt werden, in denen Eingabe- oder Ausgabeiteratoren benötigt werden, bidirektionale Iteratoren können anstelle von Vorwärtsiteratoren verwendet werden, und Random Access-Iteratoren können verwendet werden, wo Bidirektionalität erforderlich ist.

Ein zweites Merkmal, das  Iteratoren unterscheidet, besteht darin, ob sie erlauben, die Werte des ihnen verbundenen Containers zu ändern. Ein konstanter Iterator ist ein Iterator, der nur für den Zugriff auf, nicht aber für die Änderung von Werten verwendet werden kann. Ausgabeiteratoren sind niemals konstant, Eingabeiteratoren sind es dagegen immer. Die anderen Iteratoren können konstant sein oder nicht, abhängig von der Art ihrer Erzeugung. Es gibt sowohl konstante als auch nicht konstante bidirektionale Iteratoren, konstante wie nicht konstante Random Access-Iteratoren und so weiter.

Die folgende Tabelle faßt die besonderen Weisen der Erzeugung der verschiedenen Iteratorkategorien durch die Container der Standardbibliothek zusammen.

Iteratorform	Erzeugt von

Eingabeiterator	istream_iterator

Ausgabeiterator	ostream_iterator

inserter

front_inserter

back_inserter

Bidirektionaler Iterator	List

Set und Multiset

Map und Multimap

Random Access-Iterator	gewöhnliche Zeiger

Vector

Deque


Eingabeiteratoren

Eingabeiteratoren (Input-Iterator)  sind die einfachste Iteratorform. Wir wollen ihre Fähigkeiten an einem Beispielprogramm erläutern Der generische Algorithmus find() (der detailliert unter Suchoperationen beschrieben ist), führt eine einfache lineare Suche durch und sucht dabei nach einem bestimmten Wert, der sich in einem Container befindet. Der Inhalt des Containers wird mit Hilfe von zwei Iteratoren bezeichnet, die hier first und last genannt werden. Solange first ungleich last ist, wird das durch first bezeichnete Element mit dem Testwert verglichen. Wenn sich Übereinstimmung ergibt, wird der Iterator, der nun das gefundene Element bezeichnet, zurückgegeben. Ergibt sich keine Übereinstimmung, wird der Iterator first inkrementiert, und die Schleife wird ein weiteres Mal abgearbeitet. Wenn der gesamte Speicherbereich untersucht wurde, ohne daß die Suche erfolgreich wäre, liefert der Algorithmus den Bereichsendeiterator zurück.

template <class InputIterator, class T>

InputIterator

find (InputIterator first, InputIterator last, const T& value)

{

while (first != last && *first != value)

++first;

return first;

}

Dieser Algorithmus macht drei Anforderungen an einen Eingabeiterator deutlich:

Ein Iterator kann auf Gleichheit mit einem anderen Iterator geprüft werden. Die Iteratoren sind gleich, wenn sie auf die gleiche Position zeigen; andernfalls sind sie nicht gleich.

Ein Iterator kann unter Verwendung des Operators* dereferenziert werden, um den durch den Iterator bezeichneten Wert zu erhalten.

Ein Iterator kann inkrementiert werden, so daß er sich auf das nächstfolgende Element bezieht; dazu dient der Operator ++.

Beachten Sie, daß diese Merkmale in einem C++ Programm mit neuen Bedeutungen versehen werden können, da das Verhalten der angegebenen Funktionen durch Überladen der entsprechenden Operatoren verändert werden kann. Iteratoren sind durch solches Überladen überhaupt erst möglich. Es gibt drei Hauptarten von Eingabeiteratoren:

Gewöhnliche Zeiger

Gewöhnliche Zeiger können als Eingabeiteratoren verwendet werden. Da sich gewöhnliche Zeiger indizieren und inkrementieren lassen, handelt es sich praktisch um direkte Zugriffswerte; daher können gewöhnliche Zeiger entweder als Eingabe- oder als Ausgabeiteratoren verwendet werden. Der Bereichsendezeiger bezeichnet das Ende eines zusammenhängenden Speicherbereichs, und Operatoren Dereferenzierung und Inkrement haben ihre konventionelle Bedeutung. So sucht etwa das folgende Beispiel in einem Integer-Array nach dem Wert 7:

Hinweis:	Da gewöhnliche Zeiger die gleiche Funktionsweise wie Random Access-Iteratoren haben, können die meisten der generischen Algorithmen in der Standardbibliothek zusammen mit konventionellen C++ Arrays eingesetzt werden, ebenso wie mit den Containern der Standardbibliothek.

int data[100];

...

int * where = find(data, data+100, 7);

Beachten Sie, daß sich konstante Zeiger, also Zeiger, die keine Veränderung des zugrundeliegenden Arrays erlauben, einfach durch Einsetzen des Schlüsselworts const in eine Deklaration erzeugen lassen.

const int * first = data;

const int * last = data + 100;

// die vom folgenden Ausdruck zurückgegebene Stelle kann nicht verändert werden

const int * where = find(first, last, 7);

Container-Iteratoren

Alle Iteratoren, die für die verschiedenen Container, die in der Standardbibliothek zur Verfügung stehen, konstruiert werden, sind wenigstens so allgemein wie Eingabeiteratoren. Der Iterator für das erste Element in einer Kollektion wird immer von der Elementfunktion begin(), konstruiert, während der Iterator, der die "Nach-Bereichsende"-Stelle bezeichnet von der Elementfunktion end() erzeugt wird. Zum Beispiel sucht die folgende Zeile in einer Integerliste nach dem Wert 7:

list<int>::iterator where = find(aList.begin(), aList.end(), 7);

Jeder Container, der Iteratoren unterstützt, stellt in der Klassendeklaration einen Typ mit dem Namen iterator zur Verfügung. Damit lassen sich Iteratoren einheitlich in der gezeigten Weise deklarieren. Wenn der Container, auf den zugegriffen werden soll, ein konstanter Container ist, oder wenn in der Deklaration der Ausdruck const_iterator verwendet wird, dann ist der Iterator ein konstanter Iterator.

Eingabe-Stream-Iteratoren

Die Standardbibliothek bietet die Möglichkeit, mit einem Eingabeiterator auf einen Eingabe-Stream zuzugreifen. Diese Fähigkeit wird von der Klasse istream_iterator zur Verfügung gestellt; eine genauere Beschreibung finden Sie unter Eingabe-Stream-Iteratoren.


Ausgabeiteratoren

Ein Ausgabeiterator (Output-Iterator) hat die umgekehrte Funktionsweise eines Eingabeiterators. Ausgabeiteratoren können eingesetzt werden, um Werte in einer Folge zuzuweisen, aber nicht, um auf Werte zuzugreifen. Beispielsweise können wir in einem generischen Algorithmus einen Ausgabeiterator verwenden, der Werte aus einer Sequenz in eine andere kopiert:

template <class InputIterator, class OutputIterator>

OutputIterator copy

(InputIterator first, InputIterator last, OutputIterator result)

{

   while (first != last)

*result++ = *first++;

   return result;

}

Hinweis:	Eine Reihe von generischen Algorithmen bearbeitet zwei parallele Folgen. Häufig wird dabei zur Bezeichnung der zweiten Folge nur ein Anfangsiterator verwendet statt eines Iteratorenpaars. Es wird davon ausgegangen, aber nicht geprüft, daß die zweite Folge wenigstens so viele Elemente enthält wie die erste.

Hier werden zwei Bereiche bearbeitet; der Bereich der Quellwerte, der durch ein Paar von Eingabeiteratoren bezeichnet wird, und der Zielbereich. Dieser wird jedoch nur durch ein einzelnes Argument bezeichnet. Es wird angenommen, daß das Ziel groß genug ist, um alle Werte aufzunehmen. Wenn diese Annahme nicht zutrifft, treten Fehler auf.

Wie dieser Algorithmus deutlich macht, kann ein Ausgabeiterator das Element, auf das er zeigt, verändern, indem es als Ziel einer Zuweisung verwendet wird. Tatsächlich können Ausgabeiteratoren den Dereferenzierungsoperator nur auf diese Weise verwenden; sie können nicht verwendet werden, um auf die bezeichneten Elemente zuzugreifen oder sie zurückzuliefern.

Wie schon weiter oben ausgeführt, können gewöhnliche Zeiger als Beispiele für Ausgabeiteratoren - wie auch für alle anderen Iteratoren, die von Containern in der Standardbibliothek konstruiert werden - angesehen werden. (Gewöhnliche Zeiger sind Random Access-Iteratoren, die eine Obermenge der Ausgabeiteratoren darstellen). So kopiert beispielsweise das folgende Programmfragment Elemente aus einem gewöhnlichen C-Array in einen Vektor der Standardbibliothek:

int data[100];

vector<int> newdata(100);

...

copy (data, data+100, newdata.begin());

Genau so, wie der istream_iterator ein Verfahren zum Zugriff auf einen Eingabe-Stream mit dem Eingabeiterator-Mechanismus darstellt, findet sich in der Standardbibliothek auch ein Datentyp ostream_iterator, der das Schreiben von Werten in einen Ausgabe-Stream auf Iterator-ähnliche Weise gestattet. Diese Datentyp wird in Ausgabe-Stream-Iteratoren beschrieben.

Eine andere Form von Ausgabeiterator stellt der Einfügeiterator dar. Ein Einfügeiterator wandelt die Operationen Dereferenzierung/Zuweisung und das Inkrement des Ausgabeiterators in Einfügevorgänge in einen Container um. Dies ermöglicht Operationen wie copy() bei der Verwendung in Containern mit variabler Länge, wie Listen und Sets.


Vorwärtsiteratoren

Ein Vorwärtsiterator (Forward-Iterator) kombiniert die Merkmale eines Eingabeiterators und eines Ausgabeiterators. Er erlaubt sowohl den Zugriff auf als auch die Veränderung von Werten. Eine Funktion, die Vorwärtsiteratoren verwendet, ist der generische Algorithmus replace(), der bestimmte Werte durch andere ersetzt. Dieser Algorithmus ist wie folgt geschrieben:

template <class ForwardIterator, class T>

void

replace (ForwardIterator first, ForwardIterator last,

const T& old_value, const T& new_value)

{

   while (first != last) {

if (*first == old_value)

*first = new_value;

++first;

   	}

}

Gewöhnliche Zeiger können als Vorwärtsiteratoren verwendet werden, wie auch alle Iteratoren, die von Containern der Standardbibliothek konstruiert werden. Im folgenden Beispiel werden in einem Integer-Vektor alle Instanzen des Wertes 7 durch den Wert 11 ersetzt.

replace (aVec.begin(), aVec.end(), 7, 11);


Bidirektionale Iteratoren

Ein bidirektionaler Iterator (bidirectional iterator) ist einem Vorwärtsiterator ähnlich, mit dem Unterschied, daß bidirektionale Iteratoren den Dekrement-Operator (Operator --) unterstützen, und so die Bewegung durch die Elemente eines Containers in Vorwärts- oder Rückwärtsrichtung ermöglichen. Zum Beispiel kann ein bidirektionaler Iterator in einer Funktion eingesetzt werden, die die Reihenfolge der Werte in einem Container umkehrt, und das Ergebnis in einen neuen Container einsetzt.

template <class BidirectionalIterator, class OutputIterator>

OutputIterator

reverse_copy (BidirectionalIterator first,

    BidirectionalIterator last,

    OutputIterator result)

{

   while (first != last)

*result++ = *--last;

   return result;

}

Wie immer wird der Wert, der anfänglich durch das Argument last bezeichnet wird, nicht als Teil der Kollektion angesehen.

Die Funktion reverse_copy() könnte beispielsweise verwendet werden, um die Werte einer verknüpften Liste umzukehren und das Ergebnis in einen Vektor zu schreiben:

list<int> aList;

....

vector<int> aVec (aList.size());

reverse_copy (aList.begin(), aList.end(), aVec.begin() );


Random Access-Iteratoren

Einige Algorithmen benötigen eine größere Funktionalität als nur die Fähigkeit, auf Werte entweder in Vorwärts- oder in Rückwärtsschritten zuzugreifen. Iteratoren für den direkten Zugriff (Random Access-Iteratoren) erlauben den Zugriff auf Werte über Index (subscript). Indexwerte können voneinander subtrahiert werden (um die Anzahl der Elemente zwischen zwei bestimmten Werten zu erhalten) oder durch arithmetische Operationen bearbeitet werden, und zwar auf ganz ähnliche Weise wie konventionelle Zeiger.

Bei der Verwendung konventioneller Zeiger können arithmetische Operationen auf den zugrundeliegenden Speicher bezogen sein; das heißt, x+10 ist die Speicheradresse zehn Elemente nach dem Beginn von x. Bei Iteratoren bleibt hingegen die logische Bedeutung erhalten (x+10 ist das zehnte Element nach x), auch wenn die physikalischen Adressen, die bezeichnet werden, abweichen.

Zu den Algorithmen, die Iteratoren für den direkten Zugriff verwenden, zählen allgemeine Operationen wie Sortieren und Binärsuche. Beispielsweise mischt der folgende Algorithmus die Elemente eines Containers auf zufällige Weise. Dies ist - wenn auch vereinfacht - der Funktion random_shuffle() aus der Standardbibliothek ähnlich.

template <class RandomAccessIterator>

void

mixup (RandomAccessIterator first, RandomAccessIterator last)

{

while (first < last) {

iter_swap(first, first + randomInteger(last - first));

++first;

}

}

Hinweis:	Die hier beschriebene Funktion randomInteger wird in einer Reihe von Beispielprogrammen verwendet, die in späteren Abschnitten vorgestellt werden.

Die Schleife wird so lange ausgeführt, wie first eine Position bezeichnet, die in der Sequenz früher liegt, als die durch last bezeichnete Position. Nur Random Access-Iteratoren können mit Hilfe von relationalen Operatoren verglichen werden, alle anderen Operatoren können nur auf Gleichheit oder Ungleichheit verglichen werden. Bei jedem Durchgang durch die Schleife ergibt der Ausdruck last - first die Anzahl der Elemente zwischen den beiden Bereichsgrenzen. Die Funktion randomInteger() soll dabei eine Zufallszahl zwischen 0 und dem Argument generieren. Wenn der Standard-Zufallszahlengenerator verwendet wird, könnte diese Funktion zum Beispiel so geschrieben werden:

unsigned int randomInteger (unsigned int n)

// liefert zufällige Ganzzahl größer oder gleich

// 0 und kleiner als n zurück

{

return rand() % n;

}

Dieser Zufallswert wird zu dem Iterator first addiert, wodurch sich ein Iterator auf einen zufällig ausgewählten Wert im Container ergibt. Dieser Wert wird dann mit dem Element, das durch den Iterator first bezeichnet wird, vertauscht.


Reverse-Iteratoren

Ein Iterator zwingt einem zugrundeliegenden Container mit Werten naturgemäß eine Reihenfolge auf. Bei einem Vektor oder einer Map ergibt sich die Reihenfolge durch die ansteigenden Indexwerte. Bei einem Set ist es die ansteigende Reihenfolge der im Container enthaltenen Elemente. Bei einer Liste wird die Reihenfolge explizit aus der Art abgeleitet, in der die Werte eingefügt werden.

Ein Reverse-Iterator (umgekehrter Iterator) ergibt nun Werte in exakt der umgekehrten Reihenfolge der Standarditeratoren. Das heißt also, daß ein Reverse-Iterator bei einem Vektor oder einer Liste das letzte Element zuerst liefern wird und das erste Element zuletzt. Bei einem Set liefert er zuerst das größte Element und zuletzt das kleinste. Genau genommen stellen Reverse-Iteratoren keine eigene Kategorie von Iteratoren dar. Vielmehr gibt es reverse bidirektionale Iteratoren, reverse Random-Access-Iteratoren usw.

Die Datentypen List, Set und Map stellen ein Paar von Elementfunktionen zur Verfügung, das reverse bidirektionale Iteratoren erzeugt. Die Funktionen rbegin() und rend() generieren Iteratoren, die in umgekehrter Reihenfolge durch den zugrundeliegenden Container schreiten. Inkremente bewirken bei solchen Iteratoren Rückwärts-, Dekremente Vorwärtsbewegung in der Folge.

In ähnlicher Weise stellen die Datentypen Vector und Deque Funktionen zur Verfügung (die ebenfalls rbegin() und rend() heißen), die reverse Random Access-Iteratoren erzeugen. Indizierungs- und Additionsoperatoren, ebenso wie Inkremente auf solche Operatoren, bewirken eine Rückwärtsbewegung in der Folge.


Stream-Iteratoren

Stream-Iteratoren werden verwendet, um auf einen vorhandenen Eingabe- oder Ausgabe-Stream mittels Iteratoroperationen zuzugreifen.


Eingabe-Stream-Iteratoren

Hinweis:	Ein Eingabe-Stream-Iterator (Input Stream Iterator) erlaubt das Lesen eines Eingabe-Streams mit Hilfe von Iteratoroperationen. Ein Ausgabe-Stream-Iterator schreibt analog dazu beim Ausführen der Iteratoroperationen in einen Ausgabe-Stream.

Wie schon in der Erläuterung der Eingabeiteratoren angedeutet, verfügt die Standardbibliothek über ein Verfahren, das einen Eingabe-Stream in einen Eingabeiterator umwandelt. Diese Fähigkeit wird von der Klasse istream_iterator zur Verfügung gestellt. Bei der Deklaration bestehen die zwei Template-Argumente in dem Elementtyp und einem Typ, der den Abstand zwischen Elementen mißt. In nahezu allen Fällen ist dies der Standardtyp ptrdiff_t. Das einzelne Argument, das dem Konstruktor für einen istream_iterator zur Verfügung gestellt wird, ist der Stream, auf den zugegriffen werden soll. Immer, wenn der Operator ++ auf einen Eingabe-Stream-Iterator angewendet wird, wird ein neuer Wert aus dem Stream gelesen (mit dem Operator >>) und gespeichert. Dieser Wert steht dann durch Verwendung des Dereferenzierungsoperators (Operator *) zur Verfügung. Der Wert, der von istream_iterator konstruiert wird, wenn dem Konstruktor keine Argumente übergeben werden, kann als Wert für den Endeiterator verwendet werden. Der folgende Quelltext sucht zum Beispiel nach dem ersten Auftreten des Wertes 7 in einer Datei mit Integer-Werten.

istream_iterator<int, ptrdiff_t> intstream(cin), eof;

istream_iterator<int, ptrdiff_t>::iterator where =

find(intstream, eof, 7);

Das Element, das von einem Iterator für einen Eingabe-Stream bezeichnet wird, ist nur so lange gültig, bis das nächste Element im Stream angefordert wird. Ferner ist ein Eingabe-Stream-Iterator ebenfalls ein Eingabeiterator - auf Elemente kann zwar zugegriffen werden, sie können aber nicht durch Zuweisung verändert werden. Schließlich kann auf jedes Element nur einmal zugegriffen werden und nur vorwärtsschreitend. Wenn Sie den Inhalt eines Streams mehr als einmal auslesen möchten, müssen Sie für jeden Durchgang einen eigenen Iterator erzeugen.


Ausgabe-Stream-Iteratoren

Das Verfahren für den Ausgabe-Stream-Iterator (Output Stream Iterator) ist dem für den Eingabe-Stream-Iterator ähnlich. Immer, wenn dem Iterator ein Wert zugewiesen wird, wird er in den verbundenen Ausgabe-Stream geschrieben; dazu wird der Operator >> verwendet. Um einen Ausgabe-Stream-Iterator zu erzeugen, müssen Sie den verbundenen Ausgabe-Stream als Konstruktorargument angeben. Werte, die in den Ausgabe-Stream geschrieben werden, müssen die Stream-Operation >> erkennen. Ein optionales zweites Argument, das dem Konstruktor übergeben werden kann, besteht in einem String, der als Trennzeichen zwischen Wertepaaren verwendet wird. Der folgende Quelltext kopiert beispielsweise alle Werte aus einem Vektor in die Standardausgabe und trennt jeden Wert durch ein Leerzeichen vom nächsten:

copy (newdata.begin(), newdata.end(),

ostream_iterator<int> (cout, " "));

Einfache Algorithmen zur Dateibearbeitung können durch Kombination von Eingabe- und Ausgabe-Stream-Iteratoren mit den verschiedenen Algorithmen der Standardbibliothek erzeugt werden. Das folgende kurze Programm liest eine Datei mit Integerwerten aus der Standardeingabe, entfernt alle Instanzen des Wertes 7, und kopiert den Rest in die Standardausgabe, wobei es die Werte durch Zeilenschaltungen trennt.

void main()

{

istream_iterator<int, ptrdiff_t> input (cin), eof;

ostream_iterator<int> output (cout, "\n");

remove_copy (input, eof, output, 7);

}


Einfügeiteratoren

Zuweisung zu dem dereferenzierten Wert eines Ausgabeiterators wird normalerweise verwendet, um den Inhalt einer bestimmten Stelle zu überschreiben. Beispielsweise überträgt der folgende Aufruf der Funktion copy() Werte aus einem Vektor in einen anderen, obwohl der Platz für den zweiten Vektor bereits durch die Deklarationsanweisung reserviert (und sogar bereits initialisiert) war:

vector<int> a(10);

vector<int> b(10);

...

copy (a.begin(), a.end(), b.begin());

Selbst Strukturen wie etwa Listen lassen sich in dieser Weise überschreiben. Im folgenden Beispiel wird davon ausgegangen, daß die Liste mit dem Namen c wenigstens zehn Elemente umfaßt. Die anfänglichen zehn Positionen in der Liste werden durch den Inhalt des Vektors a ersetzt.

list<int> c;

...

copy (a.begin(), a.end(), c.begin());

Bei Strukturen wie Listen und Sets, die dynamisch wachsen, wenn neue Elemente hinzugefügt werden, ist es häufig sinnvoller, neue Werte in die Struktur einzufügen, statt vorhandene Positionen zu überschreiben. Ein Adaptertyp mit dem Namen Einfügeiterator (insert iterator) erlaubt die Verwendung von Algorithmen wie copy(), um Elemente in den Container einzufügen, statt Elemente des Containers zu überschreiben. Die Ausgabeoperationen des Iterators werden in Einfügevorgänge in den verbundenen Container umgewandelt. Dieser Quelltext fügt beispielsweise die Werte des Vektors a in eine zunächst leere Liste ein:

list<int> d;

copy (a.begin(), a.end(), front_inserter(d));

Es gibt drei Arten von Einfügeiteratoren; sie alle können verwendet werden, um eine Operation copy in eine Operation insert umzuwandeln. Der Iterator, der mit front_inserter erzeugt wird - siehe obiges Beispiel - fügt Werte am Anfang eines Containers ein. Der Iterator, der von back_inserter erzeugt wird, fügt Elemente am Ende eines Containers ein. Beide Arten können für Listen, Deques und sogar für Vektoren verwendet werden, aber nicht für Sets oder Maps.

Die dritte und allgemeinste Form ist inserter. Dieser Iterator akzeptiert zwei Argumente, einen Container und einen Iterator innerhalb des Containers. Diese Art kopiert Elemente an die angegebene Stelle in dem Container. (Bei Listen heißt das, Elemente werden unmittelbar vor der angegebenen Position eingefügt). Diese Art kann für alle Strukturen verwendet werden, für die auch die zuvor vorgestellten Arten funktionieren, außerdem aber auch für Sets und für Maps.

Das folgende einfache Programm gibt ein Beispiel für die Verwendung aller drei Arten von Einfügeiteratoren. Zuerst werden die Werte 3, 2 und 1 am Anfang der zunächst leeren Liste eingefügt. Beachten Sie, daß jeder Wert beim Einfügen zum neuen Anfangswert der Liste wird, so daß die sich ergebende Liste die Reihenfolge 1, 2, 3 hat. Als nächstes werden die Werte 7, 8 und 9 am Ende der Liste eingefügt. Schließlich wird die Operation find() verwendet, um einen Iterator aufzufinden, der den Wert 7 bezeichnet, und die Zahlen 4, 5 und 6 werden unmittelbar davor eingefügt. Das Ergebnis ist eine Liste der Zahlen 1 bis 9 in aufsteigender Reihenfolge.

void main() {

int threeToOne [ ] = {3, 2, 1};

int fourToSix [ ] = {4, 5, 6};

int sevenToNine [ ] = {7, 8, 9};

list<int> aList;

// zuerst am Anfang einfügen

// jeder neue Wert wird neuer Anfangswert

copy (threeToOne, threeToOne+3, front_inserter(aList));

// dann am Ende einfügen

copy (sevenToNine, sevenToNine+3, back_inserter(aList));

// Sieben finden und in der Mitte einfügen

list<int>::iterator seven = find(aList.begin(), aList.end(), 7);

copy (fourToSix, fourToSix+3, inserter(aList, seven));

// Ergebnis in die Ausgabe kopieren

copy (aList.begin(), aList.end(),

ostream_iterator<int>(cout, " "));

cout << endl;

}

Beachten Sie, daß es einen wichtigen und ziemlich feinen Unterschied gibt zwischen den Iteratoren, die von inserter(aList, aList.begin()) und front_inserter(aList) erzeugt werden. Der Aufruf von inserter(aList, aList.begin()) kopiert Werte in Folge, wobei er jeden am Anfang der Liste einfügt, während front_inserter(aList) Werte beim Kopieren jeweils zum neuen Anfangswert macht. Im Ergebnis kehrt daher front_inserter(aList) die Reihenfolge der ursprünglichen Sequenz um, während mit inserter(aList, aList.begin()) die ursprüngliche Reihenfolge beibehalten wird.


Iteratoroperationen

Die Standardbibliothek enthält zwei Funktionen, die zum Bearbeiten von Iteratoren verwendet werden können. Die Funktion advance() nimmt einen Iterator und einen numerischen Wert als Argumente an und bearbeitet den Iterator, indem sie ihn um den angegebenen Betrag verschiebt.

void advance (InputIterator & iter, Distance & n);

Für Random Access-Iteratoren ist dies gleichbedeutend mit iter + n; die Funktion ist aber sinnvoll, da sie für die Verwendung mit allen Operatorarten ausgelegt ist. Bei Vorwärtsiteratoren muß der numerische Abstand positiv sein, während der Wert für bidirektionale oder für Random Access-Iteratoren positiv oder negativ sein kann. Die Operation ist nur bei Random Access-Iteratoren effizient (konstante Ausführungszeit). In allen anderen Fällen ist sie als Schleife implementiert, die entweder den Operator ++ oder den Operator -- für den Iterator aufruft; die Ausführungszeit hängt daher proportional von der zurückgelegten Distanz ab. Die Funktion advance() prüft die Operationen an dem zugrundeliegenden Operator nicht auf Gültigkeit.

Die zweite Funktion, distance(), gibt die Anzahl der erforderlichen Iteratoroperationen zurück, die für die Bewegung von einem Element in einer Sequenz zu einem anderen erforderlich sind. Die Beschreibung dieser Funktion ist wie folgt:

void distance (InputIterator first, InputIterator last,

 Distance &n);

Das Ergebnis wird im dritten Argument zurückgeliefert, das durch Referenz übergeben wird. Distance inkrementiert diesen Wert um die Anzahl der Ausführungen des Operators ++, die für die Bewegung von first zu last erforderlich sind. Stellen Sie stets sicher, daß die durch dieses Argument übergebene Variable korrekt initialisiert ist, bevor Sie die Funktion aufrufen.


Funktionen und Prädikate

3.1 Funktionen

3.2 Prädikate

3.3 Funktionsobjekte

3.4 Negatoren und Binder


Funktionen

Eine Reihe von Algorithmen der Standardbibliothek benötigen Funktionen als Argumente . Ein einfaches Beispiel ist der Algorithmus for_each(), der für jeden Wert in einem Container eine Funktion aufruft, die als Argument übergeben wird. Das folgende Quelltextbeispiel verwendet die Funktion printElement(), um eine Ausgabe zu erzeugen, die jedes Element in einer Liste von Integerwerten beschreibt:

void printElement (int value)

{

cout << "The list contains " << value << endl;

}

main () {

list<int> aList;

...

for_each (aList.begin(), aList.end(), printElement);

}

Binäre Funktionen nehmen zwei Argumente an und werden häufig auf Werte aus zwei verschiedenen Sequenzen angewendet. Angenommen, es sind eine Liste von Strings und eine Liste mit Integerwerten vorhanden. Für jedes Element der ersten Liste soll nun der String so häufig vervielfältigt werden, wie es der entsprechende Wert in der zweiten Liste angibt. Wir können das einfach über die Funktion transform() aus der Standardbibliothek erreichen. Zunächst definieren wir eine binäre Funktion mit den gewünschten Merkmalen:

string stringRepeat (const string & base, int number)

// Basis in der angegebenen Anzahl vervielfältigen

{

string result;  // das Ergebnis ist zu Anfang leer

while (number--)  result += base;

return result;

}

Der folgende Aufruf von transform() bringt dann die gewünschte Wirkung:

list<string> words;

list<int> counts;

...

transform (words.begin(), words.end(),

counts.begin(), words.begin(), stringRepeat);

Das Anwenden der Funktion transform() mit den Werten 3, 2, 3 auf die Wörter one, two, three würde dann oneoneone, twotwo, threethreethree ergeben.


Prädikate

Ein Prädikat ist einfach eine Funktion, die entweder einen booleschen (wahr/falsch) oder einen Integerwert zurückgibt. Der normalen C-Konvention folgend, wird ein Integerwert als wahr angesehen, wenn er ungleich null ist, andernfalls als falsch. Die folgende Funktion kann als Beispiel dienen. Sie nimmt einen Integer als Argument an und gibt true zurück, wenn die Zahl ein Schaltjahr darstellt, andernfalls false:

bool isLeapYear (int year)

// true zurückgeben, wenn das Jahr ein Schaltjahr ist

{

// Jahrtausende sind Schaltjahre

if (0 == year % 1000) return true;

// Jahrhunderte aber nicht

if (0 == year % 100) return false;

// jedes vierte Jahr ist eins

if (0 == year % 4) return true;

// andernfalls nicht

return false;

}

Ein Prädikat wird beispielsweise in dem generischen Algorithmus mit dem Namen find_if() als Argument verwendet. Dieser Algorithmus liefert den ersten Wert zurück, der dem Prädikat entspricht; wird kein solches Element gefunden, gibt er den Bereichsendewert zurück. Dieser Quelltext setzt diesen Algorithmus ein und ermittelt damit das erste Schaltjahr in einer Liste von Jahren:

list<int>::iterator firstLeap =

find_if(aList.begin(), aList.end(), isLeapYear);


Funktionsobjekte

Ein Funktionsobjekt ist eine Instanz einer Klasse, die den Klammeroperator (parenthesis operator) als Elementfunktion definiert. Es gibt eine Reihe von Situationen, in denen es bequemer ist, Funktionen durch Funktionsobjekte zu ersetzen. Wenn ein Funktionsobjekt als Funktion verwendet wird, wird immer beim Aufrufen der Funktion der Klammeroperator aufgerufen.

Die folgende Klassendefinition soll das verdeutlichen:

class biggerThanThree {

public:

bool operator () (int val)

{ return val > 3; }

};

Wenn wir eine Instanz der Klasse biggerThanThree erzeugen, wird bei jedem Bezug auf dieses Objekt mit der Funktionsaufrufsyntax die Klammeroperator-Elementfunktion aufgerufen. Der nächste Schritt besteht nun in der Generalisierung dieser Klasse. Dazu fügen wir einen Konstruktor und ein konstantes Datenfeld, das von dem Konstruktor gesetzt wird, hinzu.

class biggerThan {

public:

biggerThan (int x) : testValue(x) { }

const int testValue;

bool operator () (int val)

{ return val > testValue; }

};

Das Ergebnis ist eine allgemeine Funktion "bigger than X", in der der Wert von X beim Instantiieren der Klasse festgelegt wird. Das kann beispielsweise in Form eines Arguments für eine der generischen Funktionen, die ein Prädikat benötigen, geschehen. Auf diese Art findet der folgende Quelltext den ersten Wert in einer Liste, der größer als 12 ist:

list<int>::iterator firstBig =

find_if (aList.begin(), aList.end(), biggerThan(12));

Es gibt drei besonders häufig auftretende Gründe für die Verwendung von Funktionsobjekten anstelle gewöhnlicher Funktionen. Der eine Fall liegt vor, wenn ein bestehendes Funktionsobjekt aus der Standardbibliothek anstelle einer neuen Funktion eingesetzt werden kann. Ein anderer Fall ist das Einbetten von Funktionsaufrufen in Quelltextzeilen (Inlining), um die Ausführung zu beschleunigen. Schließlich besteht die Möglichkeit, daß das Funktionsobjekt auf Statusinformationen, die von einem Objekt gehalten werden, zugreifen oder diese setzen muß. Für alle drei Fälle werden wir Beispiele vorstellen.

Die folgende Tabelle faßt die in der Standardbibliothek enthaltenen Funktionsobjekte zusammen.

Name 	Implementierte Operationen

Arithmetische Funktionen

plus

minus

times

divides

modulus

negate	Addition x + y

Subtraktion x - y

Multiplikation x * y

Division x / y

Rest x % y

Negation - x

Vergleichsfunktionen

equal_to

not_equal_to

greater

less

greater_equal

less_equal	Gleichheitsprüfung x == y

Ungleichheitsprüfung x != y

Größer-als-Vergleich x > y

Kleiner-als-Vergleich x < y

Größer-als-oder-gleich-Vergleich x >= y

Kleiner-als-oder-gleich-Vergleich x <= y

Logische Funktionen

logical_and

logical_or

logical_not	logische Konjunktion x && y

logische Disjunktion x || y

logische Negation ! x

Zwei Beispiele sollen den Einsatz der Funktionsobjekte verdeutlichen. Das erste Beispiel verwendet plus(), um die elementweise Addition zweier Listen mit Integerwerten durchzuführen und das Ergebnis in die erste Liste zurückzuschreiben. Der folgende Quelltext kann diese Aufgabe ausführen:

transform (listOne.begin(), listOne.end(), listTwo.begin(),

listOne.begin(), plus<int>() );

Das zweite Beispiel negiert jedes Element in einem Vektor boolescher Werte:

transform (aVec.begin(), aVec.end(), aVec.begin(),

logical_not<bool>() );

Hinweis:	Die Klassendefinitionen für unary_function und binary_function können mittels #including functional einbezogen werden.

Die Basisklassen, die von der Standardbibliothek in der Definition der Funktionen in der obenstehenden Tabelle verwendet werden, stehen für die Erzeugung neuer unärer und binärer Funktionsobjekte zur Verfügung. Diese Basisklassen sind wie folgt definiert:

template <class ArgType, class ResultType>

class unary_function {

typedef ArgType argument_type;

typedef ResultType result_type;

};

template <class ArgType1, class ArgType2, class ResultType>

struct binary_function {

typedef ArgType1 first_argument_type;

typedef ArgType2 second_argument_type;

typedef ResultType result_type;

};

Ein Beispiel für die Verwendung dieser Funktionen findet sich in Beispielprogramme. An dieser Stelle möchten wir eine binäre Funktion vom Typ "Widget" verwenden sowie ein Argument vom Typ integer, und sodann die Identifikationsnummer des Widgets mit dem Integerwert vergleichen. Eine Funktion, die diese Aufgabe ausführt, kann wie folgt geschrieben werden:

struct WidgetTester : binary_function<Widget, int, bool> {

public:

bool operator () (const Widget & wid, int testid) const

{ return wid.id == testid; }

};

Ein zweiter Grund, sich für die Verwendung von Funktionsobjekten anstelle von Funktionen zu entscheiden, ist die schnellere Ausführung von Programm-Code. In vielen Fällen kann der Aufruf eines Funktionsobjekts, wie etwa in den an früherer Stelle angeführten Beispielen zu Aufrufen von transform(), in-line erweitert werden. Dadurch entfällt der Zeitaufwand eines Funktionsaufrufs.

Hinweis:	Eine komplexere Anwendung eines Funktionsobjekts findet sich in dem Beispielprogramm zur Wurzelsortierung, das als Beispiel für die Verwendung des Datentyps List unter Beispielprogramm: Wurzelsortierung zu finden ist. In diesem Programm werden Referenzen in dem Funktionsobjekt initialisiert, so daß das Funktionsobjekt während der Aufrufsequenz auf lokale Werte in dem aufrufenden Programm zugreifen und diese verändern kann.

Der dritte Hauptgrund, ein Funktionsobjekt anstelle einer Funktion zu verwenden, liegt vor, wenn bei jedem Aufruf der Funktion ein in vorhergehenden Aufrufen gesetzter Status verfügbar sein muß. Ein Beispiel dafür findet sich bei der Erzeugung eines Generators, der zusammen mit dem generischen Algorithmus generate() verwendet werden soll. Ein Generator ist einfach eine Funktion, die bei jedem Aufruf einen unterschiedlichen Wert zurückliefert. Die am häufigsten verwendete Form eines Generators ist der Zufallszahlengenerator, aber es gibt andere Verwendungen für dieses Konzept. Ein Sequenzgenerator liefert einfach die Werte einer ansteigenden Folge natürlicher Zahlen (1, 2, 3, 4 usw.) zurück. Dieses Objekt kann iotaGen genannt werden - nach der ähnlichen Operation in der Programmiersprache APL - und definiert sich wie folgt:

class iotaGen {

public:

iotaGen (int start = 0) : current(start) { }

int operator () () { return current++; }

private:

int current;

};

Ein iota-Objekt behält einen aktuellen Wert bei, der vom Konstruktor gesetzt werden kann oder der Voreinstellung 0 entspricht. Immer, wenn der Funktionsaufruf-Operator aufgerufen wird, wird der aktuelle Wert zurückgegeben und zugleich inkrementiert. Unter Verwendung dieses Objekts initialisiert dann der folgende Aufruf der Funktion generate() aus der Standardbibliothek einen Vektor mit 20 Elementen mit den Werten 1 bis 20:

vector<int> aVec(20);

generate (aVec.begin(), aVec.end(), iotaGen(1));


Negatoren und Binder

Negatoren und Binder sind Funktionsadapter, die zum Erzeugen neuer Funktionsobjekte aus bereits vorhandenen verwendet werden. Sie werden in fast allen Fällen im Zusammenhang mit der Erzeugung einer Argumentliste vor dem Aufruf einer weiteren Funktion oder eines generischen Algorithmus eingesetzt.

Die Negatoren not1() und not2() nehmen ein unäres bzw. ein binäres Prädikatfunktionsobjekt als Argument an und erzeugen ein neues Funktionsobjekt, das das Komplement des Originals darstellt. Verwendet man beispielsweise das Funktionsobjekt  "Widget-Tester", das im vorhergehenden Abschnitt definiert wurde, ergibt das Funktionsobjekt:

not2(WidgetTester())

ein binäres Prädikat, das exakt die gleichen Argumente annimmt wie der Widget-Tester, und das genau dann true ist, wenn das Original false ist, beziehungsweise umgekehrt. Negatoren lassen sich nur bei Funktionsobjekten, die als Unterklassen der Klassen unary_function und binary_function deklariert wurden, verwenden.

Hinweis:	Das hier mit dem Ausdruck Binder bezeichnete Konzept heißt in anderen Zusammenhängen oft Curry. Und das nicht, wie viele glauben, weil es sich dabei um eine scharfe Sache handelt. Vielmehr heißt es nach dem Computerwissenschaftler Haskell P. Curry, der in den dreißiger Jahren von diesem Konzept in großem Umfang in einem einflußreichen Buch über Computertheorie Gebrauch machte. Curry selbst schrieb diese Idee Moses Schšnfinkel zu, was die Frage aufwirft, warum wir statt von Bindern nicht lieber von  "Schšnfinkeln" sprechen.

Ein Binder nimmt eine Funktion mit zwei Argumenten an und bindet entweder das erste oder das zweite Argument an einen bestimmten Wert, wodurch sich praktisch eine Funktion mit einem Argument ergibt. Die zugrundeliegende Funktion muß eine Unterklasse der Klasse binary_function sein. Der Binder bind1st() bindet das erste Argument, während der Binder bind2nd() das zweite Argument bindet.

Beispielsweise erzeugt der Binder bind2nd(greater<int>(), 5) ein Funktionsobjekt, das auf Werte größer als 5 prüft. Dies könnte in dem folgenden Quelltextbeispiel eingesetzt werden, das einen Iterator ergibt, der den ersten Wert größer 5 in einer Liste repräsentiert:

list<int>::iterator where = find_if(aList.begin(), aList.end(),

bind2nd(greater<int>(), 5));

Durch Kombination eines Binders mit einem Negator können wir eine Funktion erzeugen, die true ist, wenn das Argument durch 3 geteilt werden kann, andernfalls ist sie false. Diese kann dann wiederum verwendet werden, um alle Vielfachen von 3 aus einer Liste zu entfernen.

list<int>::iterator where = remove_if (aList.begin(), aList.end(),

not1(bind2nd(modulus<int>(), 3)));

Ein Binder wird verwendet, um die Widget-Nummer eines Aufrufs an die binäre Funktion WidgetTester() zu binden, wodurch sich eine Funktion mit einem Argument ergibt, die nur ein Widget als Argument annimmt. Diese wird verwendet, um das erste Widget zu finden, das dem gegebenen Widget-Typ entspricht:

list<Widget>::iterator wehave =

find_if(on_hand.begin(), on_hand.end(),

bind2nd(WidgetTester(), wid));


Container-Klassen

4.1 Überblick über Container-Klassen

4.2 Wahl des geeigneten Containers

4.3 Aspekte der Speicherverwaltung

4.4 Container-Typen, die nicht in der Standardbibliothek enthalten sind


Überblick über Container-Klassen

Die Standardbibliothek stellt nicht weniger als zehn verschiedene Container-Klassen zur Verfügung. In diesem Abschnitt werden wir kurz die verschiedenen Arten vorstellen, ihre Merkmale darlegen und erläutern, welche Überlegungen bei der Suche nach dem geeigneten Container für die Lösung eines bestimmten Problems angestellt werden sollten. Die nachfolgenden Abschnitte werden dann jeden der verschiedenen Container ausführlich behandeln.

Die folgenden Tabelle gibt einen Überblick über die zehn Container-Typen der Standardbibliothek sowie eine kurze Beschreibung ihrer wichtigsten Merkmale.

Name	Merkmale

Vector	direkter Zugriff auf Elemente, effiziente Einfügung am Ende

List	effizientes Einfügen und Entfernen an beliebiger Position

Deque	direkter Zugriff, effiziente Einfügung am Anfang oder Ende

Set	Reihenfolge der Elemente wird beibehalten, effizienter Test auf Inklusion, effiziente Einfügung und Entfernung

Multiset	Set mit mehreren Exemplaren

Map	Zugriff auf Werte über Index, effizientes Einfügen und Entfernen

Multimap	Map, die mehrfach vorhandene Indizes gestattet

Stack	Einfügung und Entfernung nur vom Anfang (oben)

Queue	Einfügung am Ende, Entfernen am Anfang

Priority Queue	effizienter Zugriff und Entfernung für größten Wert


Wahl des geeigneten Containers

Die folgenden Fragen helfen herauszufinden, welcher Container-Typ sich für die Lösung eines bestimmten Problems am besten eignet.

Wie soll auf Werte zugegriffen werden?

Wenn direkter Zugriff wichtig ist, sollte ein Vektor oder ein Deque verwendet werden. Wenn sequentieller Zugriff ausreicht, kann eine der anderen Datenstrukturen geeignet sein.

Ist die Reihenfolge, in der Werte in der Kollektion gehalten werden, wichtig?

Es gibt eine Reihe verschiedener Verfahren, um Werte zu sequenzieren. Wenn während der gesamten Lebensdauer des Containers eine strikte Beachtung der Reihenfolge erforderlich ist, dann ist die Datenstruktur Set eine naheliegende Wahl, denn in ein Set eingefügte Elemente erhalten automatisch den korrekten Platz in der Reihenfolge. Wenn andererseits diese Reihenfolge nur an einem bestimmten Punkt wichtig ist (beispielsweise am Ende einer langen Reihe von Einfügevorgängen), kann es einfacher sein, die Werte in eine Liste oder einen Vektor einzusetzen und die sich ergebende Struktur zu gegebener Zeit zu sortieren. Wenn die Reihenfolge innerhalb der Struktur mit der Reihenfolge der Einfügungen zusammenhängt, dann können ein Stack, eine Queue oder eine Liste die beste Wahl sein.

Ändert sich die Größe der Struktur während der Ausführung stark?

Falls dies zutrifft, könnten eine Liste oder ein Set die beste Wahl sein. Ein Vektor oder Deque wird auch, nachdem Elemente entfernt wurden, einen großen Puffer verwalten. Wenn, umgekehrt, die Größe einer Kollektion relativ gering schwankt, benötigt ein Vektor oder Deque weniger Arbeitsspeicher als eine Liste oder ein Set mit der gleichen Anzahl Elemente.

Kann die Größe der Kollektion abgeschätzt werden?

Die Datenstruktur Vector (Vektor) bietet die Möglichkeit, vorab einen Speicherblock von festgelegter Größe zu reservieren (mit der Elementfunktion reserve()). Diese Möglichkeit besteht bei den anderen Containern nicht.

Muß häufig auf Einbettung von Werten in der Kollektion getestet werden?

Trifft dies zu, sind die Container Set oder Map eine gute Wahl. Die Prüfung, ob ein Wert in einem Set oder einer Map enthalten ist, läßt sich in sehr wenigen Schritten durchführen (logarithmisch zur Größe des Containers). Hingegen kann die Prüfung, ob ein bestimmter Wert in einem der anderen Kollektionstypen enthalten ist, den Vergleich des Werts mit jedem Element, das sich in dem Container befindet, erfordern.

Ist die Kollektion indiziert? Kann die Kollektion als eine Folge von Index/Wert-Paaren gedacht werden?

Wenn die Indizes Ganzzahlen zwischen 0 und einer Obergrenze sind, sollten ein Vektor oder Deque verwendet werden. Wenn die Indexwerte dagegen einem anderen sortierten Datentyp entsprechen (wie etwa Zeichen, Strings oder ein benutzerdefinierter Typ), kann der Container-Typ Map verwendet werden.

Können die Werte zueinander in Beziehung gesetzt werden?

Alle Werte, die in einem der Container der Standardbibliothek gespeichert werden können, müssen auf Gleichheit mit einem anderen Wert verglichen werden können. Es müssen jedoch nicht alle mit dem relationalen Kleiner-als-Operator verglichen werden können. Wenn aber Werte nicht mit dem relationalen Kleiner-als-Operator geordnet werden können, können sie nicht in einem Set oder einer Map gespeichert werden.

Muß häufig der größte Wert der Kollektion gefunden oder entfernt werden?

Trifft dies zu, ist die Datenstruktur der Ereigniswarteschlange die beste Wahl.

An welchen Positionen sollen Werte in die Struktur eingefügt oder aus ihr entfernt werden?

Wenn Werte an beliebiger Position eingefügt werden sollen, ist eine Liste die beste Wahl. Wenn Werte nur am Anfang eingefügt werden müssen, sind Deque oder Liste vorzuziehen. Wenn Werte nur am Ende eingefügt oder entfernt werden, sind Stack oder Queue eine naheliegende Wahl.

Müssen häufig zwei oder mehr Sequenzen zu einer zusammengeführt werden?

Trifft dies zu, scheinen sich Set oder Liste anzubieten, je nachdem, ob die Reihenfolge der Kollektion gewahrt bleiben muß. Das Zusammenführen zweier Sets ist ein sehr effizienter Vorgang. Wenn die Kollektionen keine Reihenfolge haben, aber die effektive Elementfunktion splice() aus der Klasse List verwendet werden kann, ist der Datentyp List vorzuziehen, da diese Operation in den anderen Containern nicht zur Verfügung steht.

In vielen Fällen bieten sich mehrere verschiedene Container für die Lösung eines gegebenen Problems an. In diesem Fall ist eine Möglichkeit, die tatsächlichen Ausführungszeiten zu vergleichen, um die beste Alternative herauszufinden.


Aspekte der Speicherverwaltung

Die Container der Standardbibliothek können eine Vielzahl verschiedener Typen von Elementen aufnehmen. Dazu gehören die grundlegenden Datentypen (integer, char usw.), Zeiger oder benutzerdefinierte Datentypen. Container können keine Referenzen enthalten. Im allgemeinen wird die Speicherverwaltung von den Standard-Container-Klassen übernommen, und der Programmierer braucht hier nur selten aktiv zu werden.

Werte werden mit dem Kopierkonstruktor in einen Container eingesetzt. Bei den meisten Container-Klassen muß der im Container enthaltene Elementtyp auch einen Standardkonstruktor definieren. Generische Algorithmen, die in einen Container kopieren (wie etwa copy()), verwenden den Zuweisungsoperator.

Wenn ein Container im ganzen kopiert wird (beispielsweise durch den Aufruf eines Kopierkonstruktors oder als Ergebnis einer Zuweisung), wird jeder Wert in die neue Struktur kopiert. Dazu wird (in Abhängigkeit von der Struktur) entweder der Zuweisungsoperator oder ein Kopierkonstruktor verwendet. Ob das Ergebnis eine  "tiefe Kopie" (deep copy) oder eine  "flache Kopie" (shallow copy) ist, unterliegt der Kontrolle des Programmierers, der dem Zuweisungsoperator jede gewünschte Bedeutung geben kann. Speicher für Strukturen, der intern von den verschiedenen Container-Klassen verwendet wird, wird automatisch und effizient zugewiesen und freigegeben.

Wenn für den Elementtyp ein Destruktor definiert ist, wird dieser Destruktor aufgerufen, wenn Werte aus einem Container entfernt werden. Wird eine Kollektion im ganzen entfernt, wird der Destruktor für jeden verbleibenden Wert, der vom Container gehalten wird, aufgerufen.

Es sind noch einige Anmerkungen zu Containern zu machen, die Zeigerwerte enthalten. Kollektionen dieser Art sind nicht unüblich. Beispielsweise stellt eine Kollektion von Zeigern die einzige Möglichkeit dar, Werte zu speichern, die potentiell entweder Instanzen einer Klasse oder Instanzen einer Unterklasse repräsentieren können. Eine derartige Kollektion läßt sich in einem Beispielproblem finden, das unter Anwendung: Ereignisgesteuerte Simulation beschrieben ist.

In diesen Fällen ist der Container nur für den Unterhalt der eigentlichen Zeigerwerte verantwortlich. Die Verwaltung des Arbeitsspeichers für die Werte, die durch die Zeiger referenziert werden, liegt in der Verantworung des Programmierers. Dazu gehört es sicherzustellen, daß die Speicherwerte korrekt alloziiert werden (normalerweise durch Aufrufen des Operators new), daß sie nicht freigegeben werden, solange der Container Referenzen auf sie enthält, und daß sie korrekt freigegeben werden, nachdem sie aus dem Container entfernt wurden.


Container-Typen, die nicht in der Standardbibliothek enthalten sind

Es gibt eine Anzahl "klassischer" Container-Typen, die nicht in der Standardbibliothek enthalten sind. In den meisten Fällen hat das den Grund, daß die zur Verfügung stehenden Container leicht an eine Vielzahl unterschiedlicher Verwendungsmöglichkeiten angepaßt werden können, einschließlich jener, die traditionell mit diesen alternativen Kollektionen gelöst werden.

Es gibt keine Kollektion tree, die als solche bezeichnet würde. Allerdings ist der Datentyp Set intern unter Verwendung einer bestimmten Form von binärem Suchbaum implementiert. Für die meisten Probleme, die in anderen Bibliotheken mit Tree-Containern behandelt würden, stellt der Datentyp Set den passenden Ersatz dar.

Der Datentyp Set besitzt eine bestimmte Reihenfolge. Es wurden keine Verfahren vorgesehen, Mengenoperationen (Vereinigungsmenge, Teilmenge usw.) mit einer Kollektion auszuführen, die nicht sortiert werden kann (wie zum Beispiel eine Menge komplexer Zahlen). In solchen Fällen kann ersatzweise eine Liste (list) verwendet werden. Allerdings bleibt es erforderlich, besondere Funktionen für Mengenoperationen zu schreiben, da die generischen Algorithmen in diesem Fall nicht verwendet werden können.

Es gibt keine mehrdimensionalen Arrays . Vektoren können jedoch andere Vektoren als Elemente enthalten, daher lassen sich solche Strukturen leicht konstruieren.

Es gibt keinen Typ graph. Eine Darstellung des Typs Graph kann aber leicht als Map, die andere Maps enthält, konstruiert werden. Dieser Strukturtyp wird in dem Beispielproblem behandelt, das in Beispielprogramm: Graphs erläutert ist.

Es gibt keinen Typ sparse array. Eine Lösungsmöglichkeit für dieses Problem besteht in der Verwendung der Graph-Repräsentierung, die in Beispielprogramm: Graphs erläutert wird.

Es gibt keine Hash-Tabellen (Hash Table). Eine Hash-Tabelle ermöglicht Zugriff, Einfügung und Entfernung von Elementen mit konstanter Ausführungszeit. Zu diesem Zweck werden Zugriffs- und Löschvorgänge in Indizierungsoperationen umgewandelt. Hash-Tabellen können aber leicht als Vektor (oder Deque) konstruiert werden, der Listen (oder sogar Sets) als Elemente enthält. Eine ähnliche Struktur wird in dem Beispielprogramm zur Wurzelsortierung beschrieben, das in Beispielprogramm: Wurzelsortierung erläutert wird. Allerdings enthält dieses Beispiel nicht den Aufruf der Hash-Funktion zur Umwandlung eines Werts in einen Index.

Kurz gesagt: Es stehen in der Standardbibliothek zwar nicht alle denkbaren Container-Typen zur Verfügung, wohl aber jene, die für die Lösung der meisten Probleme verwendet werden. Diese bilden zugleich eine solide Basis für die Konstruktion weiterer Strukturen.


Vector

5.1 Die Datenabstraktion Vector

5.2 Vektoroperationen

5.3 Boolesche Vektoren

5.4 Beispielprogramm – Sieb des Eratosthenes


Die Datenabstraktion Vector

Die Container-Klasse Vector verallgemeinert das Konzept eines gewöhnlichen C-Arrays. Wie ein Array ist ein Vektor eine indizierte Datenstruktur, mit Indexwerten von 0 bis zu der Zahl der in der Struktur enthaltenen Elemente minus eins. Ebenfalls wie bei einem Array werden Werte einem Vektor meistens mit dem Indizierungsoperator zugewiesen, beziehungsweise aus ihm extrahiert. Jedoch bestehen zwischen dem Vektor und einem Array die folgenden wichtigen Unterschiede:

Ein Vektor verfügt über mehr "Selbstkenntnis" als ein gewöhnliches Array. Insbesondere  "kennt" ein Vektor seine Größe, die Zahl der Elemente, die er potentiell aufnehmen kann (die von seiner aktuellen Größe verschieden sein kann) usw.

Die Größe eines Vektors kann sich dynamisch ändern. Neue Elemente können am Ende eines Vektors eingefügt werden, aber auch in der Mitte. Die Speicherverwaltung ist effizient und wird automatisch vorgenommen. Allerdings sollte darauf geachtet werden, daß die Einfügung von Elementen in die Mitte eines Vektors zwar möglich ist, jedoch nicht so effizient wie bei einer Liste. (Die Datenabstraktion List). Wenn viele Einfügeoperationen erforderlich sind, sollte der List-Container anstelle des Datentyps Vector verwendet werden.

Die Container-Klasse Vektor sollte der Container-Klasse Deque gegenübergestellt und mit dieser verglichen werden. Deque wird im Detail in Datenabstraktion Deque behandelt. Wie ein Vektor ist ein Deque ( "Deck" gesprochen) eine indizierte Datenstruktur. Der Hauptunterschied liegt darin, daß ein Deque effizientes Einfügen am Anfang und am Ende des Containers erlaubt, während ein Vektor nur am Ende effiziente Einfügevorgänge ermöglicht. In vielen Situationen können beide Strukturen erfolgreich eingesetzt werden. Die Verwendung eines Vektors führt im allgemeinen zu kleineren Dateigrößen, während, je nach der besonderen Menge der durchgeführten Operationen, die Verwendung des Typs Deque ein etwas schneller ausführbares Programm ergibt.


Vector-Include-Dateien

Wenn Sie einen Vektor verwenden, müssen Sie die Header-Datei vector in Ihr Programm aufnehmen.

# include <vector>


Vektoroperationen

Die folgende Tabelle faßt die Elementfunktionen zusammen, die vom Datentyp Vector zur Verfügung gestellt werden. Jede einzelne von ihnen wird weiter unten ausführlich besprochen. Beachten Sie, daß die Elementfunktionen zwar die Grundoperationen zur Verfügung stellen, die Nützlichkeit der Datenstruktur sich aber durch die Verwendung der generischen Algorithmen, die in Überblick über generische Algorithmen und im Abschnitt 0 beschrieben sind, erheblich steigert.

Ergebnis	Name	Argumente

	vector	()
	vector vector	(size) (size, value_type)
	vector	template<class Iterator> (Iterator, Iterator)
	vector	(const vector)
	vector	template<class Iterator>assign  (Iterator, Iterator)
	vector	template<class Size, class T> assign (Size, T)
reference	at	(size_type)
value_type	back	()
RandomAccessIterator	begin	()
size_type	capacity	()
bool	empty	()
RandomAccessIterator	end	()
void	erase	(iterator)
void	erase	(iterator, iterator)
value_type	front	()
void	insert	(iterator, size_type, value_type)
iterator	insert	(iterator, value_type)
void	insert	template  <class Iterator> (iterator,Iterator, Iterator)
size_type	max_size	()
void	pop_back	()
void	push_back	(value_type)
RandomAccessIterator	rbegin	()
RandomAccessIterator	rend	()
void	reserve	(size_type)
void	resize	(size_type, value_type)
size_type	size	()
void	swap	(vector)
reference	operator[]	(size_type)
vector	operator =	(vector)
In nachfolgenden Abschnitten werden die Grundoperationen erläutert, die mit Vektoren durchgeführt werden können.


Deklaration und Initialisierung von Vektoren

Hinweis:	Elemente, die in einem Vektor enthalten sind, müssen sowohl einen Standardkonstruktor (Konstruktor ohne Argumente) als auch einen Kopierkonstruktor (copy-Konstruktor) definieren. Für einige generische Algorithmen ist es außerdem erforderlich, daß Vektorelemente entweder den Äquivalenzoperator (Operator ==) oder den relationalen Kleiner-als-Operator (Operator <) kennen, auch wenn diese Operatoren von Funktionen der Klasse Vector nicht verwendet werden.

Da es sich um ein Template-Klasse handelt, muß die Deklaration eines Vektors eine Bezeichnung des Komponententyps enthalten. Dies kann ein einfacher Sprachtyp sein (wie integer oder double), ein Zeigertyp oder ein benutzerdefinierter Typ. Im letzteren Fall muß der benutzerdefinierte Typ einen Standardkonstruktor implementieren, da dieser Konstruktor zur Initialisierung neu erzeugter Elemente benötigt wird. Für den Container-Elementtyp muß darüber hinaus auch ein Kopierkonstruktor, der explizit oder implizit definiert sein kann, vorhanden sein. Wie ein Array wird auch ein Vektor meistens mit einem Integerargument deklariert, das die Anzahl der vom Vektor gehaltenen Elemente angibt:

vector<int> vec_one(10);

Der Konstruktor, der in dieser Situation zur Erzeugung des Vektors verwendet wird, ist als explicit deklariert; dadurch wird seine Verwendung als Umwandlungsoperator verhindert. (Dieses Vorgehen ist allgemein empfehlenswert, da ein Integer sonst unter bestimmten Umständen unbeabsichtigt in einen Vektor umgewandelt werden kann).

Es gibt eine Reihe weiterer Konstruktorformen, die ebenfalls zur Erzeugung von Vektoren verwendet werden können. Über die Größe hinaus kann der Konstruktor auch einen Konstantwert zur Verfügung stellen, der dann zur Initialisierung jeder neuen Vektorposition verwendet wird. Wenn keine Größenangabe gemacht wird, enthält der Vektor zunächst keine Elemente; seine Größe nimmt dann automatisch zu, wenn Elemente hinzugefügt werden. Der Kopierkonstruktor erzeugt eine Kopie eines Vektors.

vector<int> vec_two(5, 3);	// Kopierkonstruktor

vector<int> vec_three;

vector<int> vec_four(vec_two);	//  Initialisierung durch Zuweisung

Ein Vektor kann auch unter Verwendung der Elemente aus einer anderen Kollektion initialisiert werden; dies geschieht mit Hilfe eines Anfangs-/Endeiteratorpaars. Die Argumente können jeder beliebigen Iteratorform entsprechen. Daher können Kollektionen mit Werten initialisiert werden, die aus jeder der Container-Klassen in der Standardbibliothek bezogen wurden, die Iteratoren unterstützt.

vector <int> vec_five (aList.begin(), aList.end());

Hinweis:	Zum gegenwärtigen Zeitpunkt unterstützen möglicherweise noch nicht alle Compiler die Initialisierung von Containern mittels Iteratoren, da dazu die Fähigkeit zum Definieren einer Methode mit einem vom Klassen-Template verschiedenen Template-Argument erforderlich ist. Solange die Compiler-Technik noch nicht durchgängig an die Definition der Standardbibliothek angepaßt ist, unterstützt die Rogue Wave-Implementierung der Standardbibliothek konventionelle Zeiger und Vektoriteratoren für diesen Zweck.

Einem Vektor können die Werte eines anderen Vektors zugewiesen werden; in diesem Fall wird dem Ziel eine Kopie des Argumentvektors übergeben.

vec_three = vec_five;

Die Elementfunktion assign() ist einer Zuweisung ähnlich, sie ist jedoch vielseitiger und benötigt in manchen Fällen mehr Argumente. Wie bei einer Zuweisung werden die existierenden Werte im Container gelöscht und durch die in den Argumenten angegebenen Werte ersetzt. Es gibt zwei Formen von assign(). Die erste nimmt zwei Iteratorargumente an, die eine Untersequenz eines bestehenden Containers angeben. Die Werte aus dieser Untersequenz werden dann die neuen Elemente im Empfänger. Die zweite Version von assign() akzeptiert einen Zähler und einen optionalen Wert für den Container-Elementtyp. Nach dem Aufruf enthält der Container nur die im Zähler angegebene Anzahl an Elementen, die entweder den für den Container-Typ festgelegten Standardwert haben oder den angegebenen Anfangswert.

vec_six.assign(list_ten.begin(), list_ten.end());

vec_four.assign(3, 7); // drei Exemplare von Wert 7

vec_five.assign(12); // zwölf Exemplare von Wert null

Wenn für den Container-Elementtyp ein Destruktor definiert ist, wird dieser für jeden aus der Kollektion entfernten Wert aufgerufen.

Schließlich können zwei Vektoren mittels der Operation swap() ihren gesamten Inhalt tauschen. Der Argument-Container nimmt dann die Werte des Empfängers an, während zugleich der Empfänger die Werte des Arguments annimmt. Ein swap ist sehr effizient und sollte einer elementweisen Übertragung wo immer möglich vorgezogen werden.

vec_three.swap(vec_four);


Typdefinitionen

Die Klasse Vector enthält eine Reihe von Typdefinitionen. Diese werden meistens in Deklarationsanweisungen verwendet. Beispielsweise kann ein Iterator für einen Integervektor auf diese Weise deklariert werden:

vector<int>::iterator location;

Außer iterator sind noch die folgenden Typen definiert:

value_type	Der Typ, der den vom Vektor gehaltenen Elementen assoziiert ist.
const_iterator	Ein Iterator, der keine Veränderung der zugrundeliegenden Sequenz erlaubt.
reverse_iterator	Ein Iterator, der in umgekehrter Richtung fortschreitet.
const_reverse_iterator	Eine Kombination aus konstantem und Reverse-Iterator.
reference	Ein Bezug auf ein zugrundeliegendes Element.
const_reference	Ein Bezug auf ein zugrundeliegendes Element, der keine Veränderung des Elements erlaubt.
size_type	Ein vorzeichenloser Integertyp, der für die Größenangabe von Containern verwendet wird.
difference_type	Ein Integertyp mit Vorzeichen, der für die Angabe des Abstands zwischen Iteratoren verwendet wird.


Indizieren von Vektoren

Auf den Wert, der von einem Vektor an einem bestimmten Index gehalten wird, kann - wie bei einem gewöhnlichen Array - über den Indizierungsoperator  zugegriffen werden. Und wie bei Arrays gibt es derzeit keine Gültigkeitsprüfung für Indexwerte (dies kann sich aber in späteren Versionen der Standardbibliothek ändern). Die Indizierung eines konstanten Vektors ergibt eine konstante Referenz. Versuche, einen Vektor außerhalb des gültigen Wertebereichs zu indizieren, führen zu nicht vorhersagbaren und falschen Resultaten:

cout << vec_five[1] << endl;

vec_five[1] = 17;

Die Elementfunktion at() kann anstelle des Indizierungsoperators verwendet werden. Sie nimmt exakt die gleichen Argumente wie der Indizierungsoperator an und liefert auch exakt die gleichen Werte zurück.

Die Elementfunktion front() liefert das erste Element des Vektors zurück; entsprechend gibt die Elementfunktion back() das letzte zurück. Beide geben bei der Anwendung auf konstante Vektoren auch konstante Referenzen zurück.

cout << vec_five.front() << " ... " << vec_five.back() << endl;


Ändern der Größe und des Umfangs von Vektoren

Bei Vektoren spielen drei  "Größe" eine Rolle. Die erste Art Größe ist die Anzahl der Elemente, die der Vektor aktuell hält. Die zweite ist die Maximalgröße, bis zu der ein Vektor erweitert werden kann, ohne daß dafür neuer Speicherplatz zugewiesen werden müßte. Die dritte ist die Größenobergrenze für jeden Vektor. Diese drei Werte werden von den Elementfunktionen size(), capacity() und max_size() geliefert.

cout << "size: " << vec_five.size() << endl;

cout << "capacity: " << vec_five.capacity() << endl;

cout << "max_size: " << vec_five.max_size() << endl;

Die Maximalgröße ist normalerweise nur durch den verfügbaren Speicherplatz beschränkt oder durch die Größe des Wertes, der von dem Datentyp size_type beschrieben werden kann. Die aktuelle Größe und die Kapazität sind schwieriger zu charakterisieren. Wie im nächsten Abschnitt ausgeführt wird, können Elemente einem Vektor auf verschiedene Weise hinzugefügt beziehungsweise aus ihm entfernt werden. Wenn Elemente aus einem Vektor entfernt werden, wird der Speicher für den Vektor im allgemeinen nicht neu zugewiesen. D.h. seine Größe verringert sich zwar, die Kapazität bleibt aber gleich. Eine nachfolgende Einfügung macht daher keine Neuzuweisung von Arbeitsspeicher erforderlich, sofern die ursprüngliche Kapazität nicht überschritten wird.

Hinweis:	Ein Vektor speichert Werte in einem einzigen großen Speicherblock. Ein Deque verwendet demgegenüber eine Reihe kleinerer Blöcke. Dieser Unterschied kann auf Systemen, bei denen die Größe der einzelnen Speicherblöcke beschränkt ist, wichtig sein. Auf solchen Systemen kann dann ein Deque wesentlich größere Kollektionen enthalten, als sie mit einem Vektor möglich sind.

Eine Einfügung, die zu einer Größe des Vektors jenseits der Kapazitätsgrenze führt, bringt im allgemeinen die Zuweisung eines neuen Speicherblocks mit sich, der die Vektorelemente aufnimmt. Die Werte werden dann in den neuen Speicherbereich kopiert. Dazu wird der zum Elementtyp passende Zuweisungsoperator verwendet. Anschließend wird der alte Speicher gelöscht. Da dieser Vorgang unter Umständen aufwendig sein kann, hat der Programmierer beim Datentyp Vector die Möglichkeit, einen Wert für die Kapazität eines Vektors festzulegen. Die Funktion reserve() weist den Vektor an, zumindest bis zur angegebenen Größe zu wachsen. Wenn das mit reserve() verwendete Argument größer als die aktuelle Kapazität ist, findet eine Neualloziierung statt, und die neue Kapazität entspricht dem Argumentwert. (Der Vektor kann in der Folge durchaus noch größer werden; der als Argument übergebene Wert stellt keine Obergrenze dar, sondern lediglich eine Vermutung). Wenn die Kapazität bereits größer ist als der Argumentwert, findet keine Neualloziierung statt. Der Aufruf von reserve() ändert weder die Größe des Vektors noch die Elementwerte (mit der Ausnahme, daß sie unter Umständen verschoben werden, wenn eine Neualloziierung durchgeführt werden muß).

vec_five.reserve(20);

Bei einer Neualloziierung werden alle Referenzen, Zeiger und Iteratoren, die sich auf die vom Vektor gehaltenen Elemente beziehen, ungültig.

Die Elementfunktion empty() liefert true zurück, wenn der Vektor aktuell die Größe null hat (unabhängig von der Kapazität des Vektors). Im allgemeinen ist der Gebrauch dieser Funktion effizienter als ein Vergleich des Rückgabewertes von size() mit null.

cout << "empty is " << vec_five.empty() << endl;

Die Elementfunktion resize() ändert die Größe des Vektors auf den im Argument angegebenen Wert. Je nach Bedarf werden am Ende der Kollektion Werte hinzugefügt oder gelöscht. Optional kann ein zweites Argument verwendet werden, das den Initialwert für alle neu in die Kollektion eingefügten Elemente angibt. Wenn für den Elementtyp ein Destruktor definiert ist, wird dieser für alle Elemente aufgerufen, die aus der Kollektion entfernt werden.

// Größe 12 annehmen, erforderlichenfalls Elemente mit dem Wert 17 einfügen

vec_five.resize (12, 17);


Einfügen und Entfernen von Elementen

Wie schon früher ausgeführt, unterscheidet sich die Klasse Vector von einem gewöhnlichen Array unter anderem dadurch, daß ein Vektor unter bestimmten Umständen seine Größe ändern kann. Wenn durch eine Einfügung die Zahl der im Vektor gehaltenen Elemente die Kapazität des aktuell zugewiesenen Speicherblocks übersteigt, wird ein neuer Block zugewiesen, und die Elemente werden in den neuen Speicherblock kopiert.

Hinweis:	Selbst das Hinzufügen eines einzelnen Elements kann, im ungünstigsten Fall, einen Zeitaufwand verursachen, der proportional zu der Anzahl der Elemente im Vektor ist - wenn nämlich jedes Element an eine neue Stelle verschoben oder gar kopiert werden muß. Wenn Ihr aktuelles Problem in großem Umfang zu Einfügeoperationen führt, sollten Sie die Möglichkeit untersuchen, Container wie Listen oder Sets zu verwenden, die auf Einfügevorgänge optimiert sind.

Mit der Funktion push_back(). kann am Ende eines Vektors ein neues Element hinzugefügt werden. Wenn der Platz in dem aktuellen Speicherblock ausreicht, ist diese Operation sehr effizient (konstante Ausführungszeit).

vec_five.push_back(21);	// Element 21 am Ende der Kollektion hinzufügen

Die entsprechende Entfernungsoperation ist pop_back(),, die die Größe des Vektors verringert, aber seine Kapazität nicht ändert. Wenn der Container-Typ einen Destruktor definiert, wird dieser für den zu löschenden Wert aufgerufen. Auch diese Operation ist sehr effizient (Die Klasse Deque gestattet das Einfügen und Löschen von Werten sowohl am Anfang als auch am Ende der Kollektion. Diese Funktionen sind unter Datenabstraktion Deque beschrieben; dort sind Deques im Detail erläutert).

Allgemeinere Einfügeoperationen können mit der Elementfunktion insert() vorgenommen werden. Die Einfügeposition wird von einem Iterator bezeichnet; die Einfügung findet dann unmittelbar vor der bezeichneten Position statt. Eine feststehende Anzahl konstanter Elemente kann mit einem einzigen Funktionsaufruf eingefügt werden. Es ist wesentlich effizienter, einen Block von Elementen mit einem einzigen Funktionsaufruf einzufügen, als eine Folge von Einzelvorgängen auszuführen. Bei einem einzigen Aufruf wird nämlich höchstens eine Neualloziierung von Speicher vorgenommen.

// Position der 7 suchen

vector<int>::iterator where =

find(vec_five.begin(), vec_five.end(), 7);

// dann die 12 vor der 7 einfügen

vec_five.insert(where, 12);

vec_five.insert(where, 6, 14); 	// sechs Exemplare von 14 einfügen

Die allgemeinste Form der Elementfunktion insert() nimmt eine Position und ein Iteratorpaar an, das eine Untersequenz aus einem anderen Container bezeichnet. Der von der Sequenz bezeichnete Bereich von Werten wird in den Vektor eingefügt. Auch hier gilt, daß die Verwendung dieser Funktion einer Folge von einzelnen Einfügeoperationen vorzuziehen ist, weil höchstens einmal neu alloziiert werden muß.

vec_five.insert (where, vec_three.begin(), vec_three.end());

Hinweis:	Es sei nochmals darauf hingewiesen: Sollte als Ergebnis einer Einfügung eine Neualloziierung stattfinden, werden dadurch alle Referenzen, Zeiger und Iteratoren ungültig, die eine Position in dem nunmehr gelöschten Speicherblock bezeichneten, der die Werte vor der Neualloziierung enthielt.

Ergänzend zu der Elementfunktion pop_back(), die Elemente vom Ende eines Vektors entfernt, existiert eine Funktion, die Elemente aus der Mitte eines Vektors entfernt und einen Iterator verwendet, um die Position anzugeben. Diese Elementfunktion hat den Namen erase(). Es existieren zwei Formen; die erste akzeptiert einen einzelnen Iterator und entfernt einen Einzelwert, die zweite nimmt ein Iteratorpaar an und entfernt alle Werte im angegebenen Bereich. Die Größe des Vektors verringert sich, seine Kapazität bleibt aber unverändert. Wenn der Container-Typ einen Destruktor definiert, wird dieser für die gelöschten Werte aufgerufen.

vec_five.erase(where);

// von 12 bis zum Ende löschen

where = find(vec_five.begin(), vec_five.end(), 12);

vec_five.erase(where, vec_five.end());


Iteration

Die Elementfunktionen begin() und end() ergeben Random Access-Iteratoren für den Container. Es soll nochmals darauf hingewiesen werden, daß diese Iteratoren nach dem Einfügen oder Entfernen von Elementen ungültig sein können. Die Elementfunktionen rbegin() und rend() liefern ähnliche Iteratoren zurück, jedoch greifen diese in umgekehrter Reihenfolge auf die zugrundeliegenden Elemente zu. Konstante Iteratoren werden dann zurückgeliefert, wenn der ursprüngliche Container als konstant deklariert ist, oder wenn das Ziel der Zuweisung oder des Parameters konstant ist.


Vektor: Testen auf enthaltene Elemente
Vektor: Testen auf Inklusion

Ein Vektor stellt keine Methode zur Verfügung, mit der direkt festgestellt werden kann, ob ein bestimmter Wert in einer Kollektion enthalten ist. Es können aber die generischen Algorithmen find() oder count() (Suchen eines Elements, das einer Bedingung genügt und Zählen der Elemente, die einer Bedingung genügen) für diesen Zweck verwendet werden. Beispielsweise prüft die folgende Anweisung, ob ein Vektor das Element 17 enthält.

Hinweis:	Beachten Sie bitte, daß count() sein Ergebnis durch ein Argument zurückliefert, das durch Referenz übergeben wird. Dieser Wert muß vor dem Aufrufen dieser Funktion unbedingt korrekt initialisiert werden.

int num = 0;

count (vec_five.begin(), vec_five.end(), 17, num);

if (num)

cout << "contains a 17" << endl;

else

cout << "does not contain a 17" << endl;


Sortieren und Operationen für sortierte Vektoren

Ein Vektor hält seine Elemente nicht automatisch in einer bestimmten Reihenfolge. Jedoch kann in einem Vektor mit dem generischen Algorithmus sort() (Sortieralgorithmen) eine Reihenfolge hergestellt werden. Die einfachste Form von sort verwendet für den Vergleich den Kleiner-als-Operator für den Elementtyp. Eine alternative Version des generischen Algorithmus erlaubt dem Programmierer, den Vergleichsoperator explizit anzugeben. Diese Version kann beispielsweise verwendet werden, um die Elemente in absteigender statt in aufsteigender Reihenfolge zu sortieren:

// aufsteigen sortieren

sort (aVec.begin(), aVec.end());

// absteigend sortieren, Sortierfunktion explizit angegeben

sort (aVec.begin(), aVec.end(), greater<int>() );

// alternative Methode für absteigende Sortierreihenfolge

sort (aVec.rbegin(), aVec.rend());

Eine Reihe der unter Überblick über Algorithmen für geordnete Kollektionen beschriebenen Operationen kann auf einen Vektor, der eine sortierte Kollektion enthält, angewandt werden. Beispielsweise können zwei Vektoren mit dem generischen Algorithmus merge() (Zusammenführen von geordneten Sequenzen) zusammengeführt werden.

// zwei Vektoren zusammenführen und Ausgabe anzeigen

merge (vecOne.begin(), vecOne.end(), vecTwo.begin(), vecTwo.end(),

ostream_iterator<int> (cout, " "));

Das Sortieren eines Vektors erlaubt auch den Einsatz der effizienteren binären Suchalgorithmen (Binärsuche) anstelle eines linear fortschreitenden Algorithmus wie find().


Nützliche generische Algorithmen

Die meisten Algorithmen, die unter Überblick über generische Algorithmen beschrieben sind, können mit Vektoren verwendet werden. Die folgende Tabelle faßt einige der nützlichsten zusammen. Zum Beispiel läßt sich der Maximalwert, der in einem Vektor enthalten ist, wie folgt ermitteln:

vector<int>::iterator where =

max_element (vec_five.begin(), vec_five.end());

cout << "maximum is " << *where << endl;

Verwendung	Name

Einen Vektor mit einem angegebenen	fill
Initialwert füllen

Eine Sequenz in eine andere kopieren	copy

Werte aus einem Generator in einen	generate
Vektor kopieren

Ein Element suchen, das einer	find
Bedingung entspricht

Aufeinanderfolgende doppelte Elemente	adjacent_find
suchen

Eine Untersequenz innerhalb eines	search
Vektors suchen

Position des größten oder kleinsten	max_element,
Elements ermitteln	min_element

Reihenfolge der Elemente umkehren 	reverse

Elemente durch neue Werte ersetzen	replace

Elemente um einen Mittelpunkt rotieren	rotate

Elemente in zwei Gruppen aufteilen	partition

Permutationen erzeugen	next_permutation

Zusammenführen innerhalb eines Vektors	Inplace_merge

Elemente im Vektor zufällig mischen	random_shuffle

Elemente zählen, die einer Bedingung	count
genügen

Vektor auf einen einzelnen Wert	accumulate
verkleinern

Inneres Produkt zweier Vektoren	inner_product

Zwei Vektoren paarweise auf	equal
Gleichheit prüfen

Lexikalischer Vergleich	lexicographical_compare

Transformation auf einen Vektor anwenden	transform

Teilsummen von Werten	partial_sum

Benachbarte Wertunterschiede	adjacent_difference

Funktion für jedes Element ausführen	for_each


Boolesche Vektoren

Vektoren aus Bit-Werten (boolesche 1/0-Werte) werden in der Standardbibliothek als Sonderfall behandelt, damit mehrere Elemente zu einem Wort verdichtet werden können. Die Operationen für einen booleschen Vektor (boolean vector), vector<bool>, stellen eine Obermenge der Operationen für einen gewöhnlichen Vektor dar, ihre Implementation ist aber effizienter.

Eine neue Elementfunktion, die für den Datentyp boolean vector hinzugefügt wurde, ist flip(). Diese Funktion kehrt alle Bits des Vektors um. Boolesche Vektoren geben darüber hinaus einen internen Wert als Referenz zurück, der auch die Elementfunktion flip() unterstützt.

vector<bool> bvec(27);

bvec.flip();        // alle Werte umkehren

bvec[17].flip();      // Bit 17 umkehren

vector<bool> unterstützt außerdem eine zusätzliche Elementfunktion swap().


Beispielprogramm: Sieb des Eratosthenes

Hinweis:	Der Quelltext für dieses Programm befindet sich in der Datei sieve.cpp.

Ein Beispielprogramm, das die Verwendung von Vektoren illustriert, ist der klassische Algorithmus, der unter dem Namen Sieb des Eratosthenes bekannt ist und für das Erkennen von Primzahlen verwendet wird. Eine Liste aller Zahlen bis zu einer bestimmten Obergrenze ist durch einen Integervektor repräsentiert. Die Grundidee ist, alle Zahlen auszustreichen (auf null zu setzen), die keine Primzahlen sein können; auf diese Weise sind dann alle verbleibenden Werte Primzahlen. Zu diesem Zweck untersucht eine Schleife alle Zahlen nacheinander, und streicht bei allen, die auf eins gesetzt sind (und damit noch nicht aus der Menge der Primzahlkandidaten ausgeschlossen wurden), alle Vielfachen der Zahl aus. Wenn die äußere Schleife beendet ist, sind alle verbleibenden Primzahlen entdeckt worden. Dies ist das Programm:

void main() {

// ein Sieb aus Integerzahlen erzeugen, das zu Beginn gesetzt ist

const int sievesize = 100;

vector<int> sieve(sievesize, 1);

// jetzt auf 1-Bit-Positionen suchen

for (int i = 2; i * i < sievesize; i++)

if (sieve[i])

for (int j = i + i; j < sievesize; j += i)

sieve[j] = 0;

// schließlich die gesetzten Werte ausgeben

for (int j = 2; j < sievesize; j++)

if (sieve[j])

cout << j << " ";

cout << endl;

}


List

6.1 Die Datenabstraktion List

6.2 Listenoperationen

6.3 Beispielprogramme


Die Datenabstraktion List

Die Datenstruktur Vector ist ein Container von relativ feststehender Größe. Zwar stellt die Standardbibliothek Verfahren für das Ändern der Größe eines Vektors zur Laufzeit zur Verfügung, aber derartige Operationen sind aufwendig und sollten nur selten durchgeführt werden. Aber bei vielen Problemen läßt sich die Größe einer Kollektion schwer vorhersagen oder schwankt während der Ausführung in einem weiten Bereich. In solchen Fällen sollte eine andere Datenstruktur eingesetzt werden. In diesem Abschnitt werden wir eine alternative Datenstruktur erläutern, die bei derartigen Umständen verwendet werden kann, den Datentyp List.

Eine Liste (list) entspricht dem intuitiven Konzept, Elemente in einer linearen (wiewohl nicht notwendigerweise geordneten) Sequenz zu halten. Neue Werte können entweder am Anfang oder am Ende der Liste hinzugefügt oder entfernt werden. Wenn ein Iterator verwendet wird, um eine Position zu bezeichnen, können Elemente auch in der Mitte der Liste eingefügt oder entfernt werden. In allen Fällen sind die Einfüge- und Löschvorgänge effizient; sie werden in konstanter Zeit ausgeführt, die von der Anzahl der Elemente, die sich in der Kollektion befinden, unabhängig ist. Schließlich ist eine Liste eine lineare Struktur. Auf den Inhalt einer Liste kann nicht über einen Index zugegriffen werden; im allgemeinen kann auf Elemente nur mittels linearen Durchgehens aller Werte zugegriffen werden.


List-Include-Dateien

Immer, wenn Sie den Datentyp List verwenden, müssen Sie die Header-Datei list in Ihr Programm aufnehmen.

# include <list>


Listenoperationen

Die folgende Tabelle faßt die Elementfunktionen zusammen, die vom Datentyp List zur Verfügung gestellt werden. Jede von ihnen wird weiter unten ausführlich vorgestellt. Beachten Sie, daß die Elementfunktionen zwar die Grundoperationen zur Verfügung stellen, die Nützlichkeit der Datenstruktur sich aber durch die Verwendung der generischen Algorithmen, die in Überblick über generische Algorithmen und Überblick über Algorithmen für geordnete Kollektionen. beschrieben sind, erheblich steigert.

Ergebnis	Name	Argumente

	list	()
	list list	(size) (size, value_type)
	list	template <class Iterator> (Iterator, Iterator)
	list	(const list)
	list	template<class Iterator> assign (Iterator, Iterator)
	list	template<class Size,class T> assign (Size, T)
value_type	back	()
BidirectionalIterator	begin	()
bool	empty	()
BidirectionalIterator	end	()
void	erase	(iterator)
void	erase	(iterator, iterator)
value_type	front	()
iterator	insert	(iterator, size_type, value_type)
iterator	insert	(iterator, value_type)
void	insert	template <class Iterator> (iterator, Iterator, Iterator)
size_type	max_size	()
void	merge	(list)
void	pop_back	()
void	pop_front	()
void	push_back	(value_type)
void	push_front	(value_type)
BidirectionalIterator	rbegin	()
void void	remove remove_if	(value_type) (predicate)
BidirectionalIterator	rend	()
void	reverse	()
size_type	size	()
void	sort	()
void void void	splice splice splice	(iterator, list) (iterator, list, iterator) (iterator, list, iterator iterator)
void	swap	(list)
void void	unique unique	() (predicate)
list	operator =	(list)
In den nachfolgenden Abschnitten werden die Grundoperationen erläutert, die mit Listen durchgeführt werden können.


Deklaration und Initialisierung von Listen

Hinweis:	Beachten Sie, daß bei der Deklaration eines Containers für die Aufnahme von Zeigern, die Verantwortung für die Verwaltung des Speichers für die Objekte, auf die gezeigt wird, bei Ihnen selbst liegt. Die Container-Klassen können beispielsweise nicht automatisch Speicher für diese Objekte freigeben, wenn ein Element aus dem Container entfernt wird.

Eine Liste (list) kann auf verschiedene Weise deklariert werden. In der einfachsten Form wird eine Liste einfach durch Festlegen des Elementtyps, den die Kollektion enthalten soll, deklariert. Dies kann ein einfacher Sprachtyp sein (wie integer oder double), ein Zeigertyp oder ein benutzerdefinierter Typ. In diesem Fall muß der benutzerdefinierte Typ einen Standardkonstruktor (einen Konstruktor ohne Argumente) implementieren, da er in manchen Fällen für die Initialisierung neu erzeugter Elemente benötigt wird. Eine in dieser Weise deklarierte Kollektion enthält anfänglich keine Elemente.

list <int> list_one;

list <Widget *> list_two;

list <Widget> list_three;

Eine andere Form der Deklaration erzeugt eine Kollektion, die bereits zu Anfang eine gewisse Zahl gleicher Elemente enthält. Der Konstruktor für diese Form wird als explicit deklariert, was bedeutet, daß er nicht als Umwandlungsoperator verwendet werden kann. Dies verhindert eine unbeabsichtigte Umwandlung von Integerwerten in Listen. Der Konstruktor für diese Form nimmt zwei Argumente an, eine Größe und einen Initialwert. Das zweite Argument ist optional. Wenn nur die Zahl der anfangs zu erzeugenden Elemente angegeben wird, werden diese Werte mit dem Standardkonstruktor initialisiert. Andernfalls werden sie mit dem Wert des zweiten Arguments initialisiert:

list <int> list_four (5); 	// 5 Elemente, zu Null initialisiert

list <double> list_five (4, 3.14); 	// 4 Werte, zu Anfang 3.14

list <Widget> wlist_six (4); 	// Standardkonstruktor, 4 Elemente

list <Widget> list_six (3, Widget(7)); 	// 3 Exemplare von Widget(7)

Listen können auch unter Verwendung der Elemente aus einer anderen Kollektion initialisiert werden; dies geschieht mit Hilfe eines Anfangs-/Endeiteratorpaars. Die Argumente können jeder beliebigen Iteratorform entsprechen. Daher können Kollektionen mit Werten initialisiert werden, die aus jeder der Container-Klassen in der Standardbibliothek bezogen wurden, die Iteratoren unterstützt. Einige Compiler unterstützen diese Möglichkeit unter Umständen noch nicht, da für dieses Vorgehen die Spezialisierung einer Elementfunktion unter Verwendung eines Templates erforderlich ist. In diesen Fällen kann ein alternatives Verfahren, das den generischen Algorithmus copy() verwendet, eingesetzt werden. Wenn eine Liste mit copy() initialisiert wird , muß ein Einfügeiterator (insert iterator) konstruiert werden, um die Ausgabeoperationen, die von dem Algorithmus copy vorgenommen werden, in Einfügevorgänge in der Liste umzuwandeln (siehe dazu auch Einfügeiteratoren). Der Einfügeiterator benötigt zwei Argumente, erstens die Liste, in die der Wert eingefügt werden soll, zweitens einen Iterator, der die Position für die Einfügung angibt. Einfügeiteratoren können auch verwendet werden, um Elemente an eine zufällige Position in einer existierenden Liste zu kopieren.

list <double> list_seven (aVector.begin(), aVector.end());

// das Folgende ist mit dem Obenstehenden äquivalent

list <double> list_eight;

copy (aVector.begin(), aVector.end(),

inserter(list_eight, list_eight.begin()));

Die Operation insert(), die in Einsetzen von Elementen in Listen beschrieben wird, kann auch verwendet werden, um Werte, die durch einen Iterator bezeichnet werden, in eine Liste einzufügen. Einfügeiteratoren können benutzt werden, um eine Liste mit einer Sequenz von Werten zu initialisieren, die von einem Generator erzeugt wurden (siehe dazu auch Initialisieren von Sequenzen mit generierten Werten). Diese Quelltextzeilen verdeutlichen das:

list <int> list_nine;	// Liste mit 1 2 3 ... 7 initialisieren

generate_n (inserter(list_nine, list_nine.begin()),

7, iotaGen(1));

Ein Kopierkonstruktor kann verwendet werden, um eine Liste mit Werten zu initialisieren, die aus einer anderen Liste stammen. Der Zuweisungsoperator führt die gleichen Aktionen aus. In beiden Fällen wird der Zuweisungsoperator für den Elementtyp verwendet, um jeden neuen Wert zu kopieren.

list <int> list_ten (list_nine);	// Kopierkonstruktor

list <Widget> list_eleven;

list_eleven = list_six;	// Werte durch Zuweisung kopieren

Die Elementfunktion assign() ist dem Zuweisungsoperator ähnlich. Sie ist aber flexibler und benötigt, in manchen Fällen, mehr Argumente. Wie bei einer Zuweisung werden die im Container existierenden Werte gelöscht und durch die in den Argumenten angegebenen Werte ersetzt. Wenn für den Container-Elementtyp ein Destruktor zur Verfügung steht, wird er für die zu entfernenden Elemente aufgerufen. Es gibt zwei Formen von assign(). Die erste nimmt zwei Iteratorargumente an, die eine Untersequenz in einem vorhandenen Container angeben. Die Werte in dieser Untersequenz werden dann zu den neuen Elementen im Empfänger. Die zweite Version von assign akzeptiert einen Zähler und einen optionalen Wert vom Container-Elementtyp. Nach dem Aufruf enthält der Container dann die im Zähler festgelegte Elementanzahl, und deren Wert entspricht entweder dem Standardwert des Container-Typs oder dem angegebenen Initialwert.

list_six.assign(list_ten.begin(), list_ten.end());

list_four.assign(3, 7); 	// drei Exemplare von Wert 7

list_five.assign(12); 	// zwölf Exemplare von Wert null

Schließlich können zwei Listen ihren gesamten Inhalt mittels der Operation swap() tauschen. Der Argument-Container nimmt dann die Werte des Empfängers an, und der Empfänger übernimmt die Werte des Arguments. Ein swap ist sehr effizient, und er sollte wo immer möglich einem expliziten elementweisen Transfer vorgezogen werden.

list_ten.swap(list_nine); 	// Listen neun und zehn tauschen


Typdefinitionen

Die Klasse List enthält eine Reihe von Typdefinitionen. Diese werden meistens in Deklarationsanweisungen verwendet. Beispielsweise kann ein Iterator für eine Integerliste auf die folgende Weise deklariert werden:

list<int>::iterator location;

Außer iterator sind in List die folgenden Typen definiert:

value_type	Der Typ, der mit den in der Liste enthaltenen Elementen verbunden ist.
const_iterator	Ein Iterator, der keine Veränderung der zugrundeliegenden Sequenz erlaubt.
reverse_iterator	Ein Iterator, der in umgekehrter Richtung fortschreitet.
const_reverse_iterator	Eine Kombination aus konstantem und Reverse-Iterator.
reference	Eine Referenz auf ein zugrundeliegendes Element.
const_reference	Eine Referenz auf ein zugrundeliegendes Element, die keine Veränderung des Elements zuläßt.
size_type	Ein vorzeichenloser Integertyp, der für die Größenangabe von Containern verwendet wird.
difference_type	Ein Integertyp mit Vorzeichen, der für die Angabe des Abstands zwischen Iteratoren verwendet wird.


Einsetzen von Elementen in Listen

Werte können auf verschiedene Weise in eine Liste eingesetzt werden. Meistens werden Elemente einer Liste am Anfang oder am Ende hinzugefügt. Diese Aufgaben werden von den Operationen push_front() und push_back() durchgeführt. Diese Operationen sind für beide Container-Typen effizient (konstante Ausführungszeit).

list_seven.push_front(1.2);

list_eleven.push_back (Widget(6));

An einer früheren Stelle (Einfügeiteratoren) haben wir dargelegt, wie Werte mit Hilfe eines Einfügeiterators und des generischen Algorithmus copy() oder generate() in einer Liste an einer Position eingefügt werden können, die durch einen Iterator bezeichnet wird. Es gibt darüber hinaus eine Elementfunktion insert(), die die Notwendigkeit zur Konstruktion eines Einfügeiterators umgeht. Weiter unten wird ausgeführt, daß die Rückgabewerte der Iterator-Generatorfunktionen begin() und end() den Anfang und das Ende einer Liste bezeichnen. Mit ihnen kann ein den Operationen push_front() oder push_back() äquivalenter Einfügevorgang durchgeführt werden. Wenn nur ein Iterator festgelegt wird, wird der Standardelementwert eingesetzt.

// Standard-Widget am Anfang der Liste einfügen

list_eleven.insert(list_eleven.begin());

// Widget 8 am Ende der Liste einfügen

list_eleven.insert(list_eleven.end(), Widget(8));

Hinweis:	Im Gegensatz zu einem Vektor oder einem Deque führen Einfüge- und Löschvorgänge aus der Mitte einer Liste nicht zu ungültigen Referenzen oder Zeigern auf andere Elemente im Container. Diese Eigenschaft kann wichtig sein, wenn zwei oder mehr Iteratoren für den Bezug auf den gleichen Container verwendet werden.

Ein Iterator kann eine Position in der Mitte einer Liste bezeichnen. Es gibt verschiedene Verfahren, diesen Iterator zu erhalten. Beispielsweise läßt sich das Ergebnis einer jeden Suchoperation verwenden, die unter Suchoperationen beschrieben sind, etwa ein Aufruf des generischen Algorithmus find(). Der neue Wert wird unmittelbar vor der vom Iterator bezeichneten Position eingefügt. Die Operation insert() liefert ihrerseits einen Iterator zurück, der die Position des eingefügten Wertes angibt. Dieser Ergebniswert wird in den oben gezeigten Aufrufen ignoriert.

// Position des ersten Werts 5 in Liste suchen

list<int>::iterator location =

find(list_nine.begin(), list_nine.end(), 5);

// und eine 11 unmittelbar davor einfügen

location = list_nine.insert(location, 11);

Es ist auch möglich, eine feststehende Anzahl Exemplare eines Argumentwerts einzufügen. Diese Form von insert() liefert die Position der Werte nicht zurück.

line_nine.insert (location, 5, 12); 	// fünf Zwölfen einfügen

Schließlich kann eine vollständige Sequenz, die durch ein Iteratorpaar bezeichnet wird, in eine Liste eingefügt werden. Auch in diesem Fall wird von insert () kein verwendbarer Wert als Ergebnis der Einfügung zurückgeliefert.

// den gesamten Inhalt von list_ten in list_nine einfügen

list_nine.insert (location, list_ten.begin(), list_ten.end());

Es gibt verschiedene Möglichkeiten, eine Liste in eine andere einzukleben. Ein Einklebevorgang (splice) unterscheidet sich von einem Einfügevorgang dadurch, daß das Element der Empfängerliste hinzugefügt und zugleich aus der Argumentliste entfernt wird. Aus diesem Grund kann ein Einklebevorgang sehr effizient durchgeführt werden, und sollte wann immer möglich verwendet werden. Ebenso wie eine Einfügung verwendet auch die Elementfunktion splice() einen Iterator, um die Stelle in der Empfängerliste anzuzeigen, an der die Einklebung vorgenommen werden soll. Das Argument ist entweder eine vollständige Liste, ein einzelnes Element in einer Liste (durch einen Iterator bezeichnet) oder eine Untersequenz einer Liste (durch ein Iteratorpaar bezeichnet).

// das letzte Element aus Liste ten einkleben

list_nine.splice (location, list_ten, list_ten.end());

// die gesamte Liste ten einkleben

list_nine.splice (location,  list_ten);

// Liste 9 wiederum in Liste 10 einkleben

list_ten.splice (list_ten.begin(), list_nine,

list_nine.begin(), location);

Zwei geordnete Listen können mit der Operation merge() zu einer kombiniert werden. Werte aus der Argumentliste werden in die geordnete Liste eingeführt, die Argumentliste wird dabei geleert. Diese Zusammenführung ist stabil; das heißt, die Elemente behalten ihre relative Ordnung aus den Originallisten. Wie bei dem generischen Algorithmus gleichen Namens (Zusammenführen von geordneten Sequenzen) werden zwei Formen unterstützt. Die zweite Form verwendet die als Argument übergebene binäre Funktion zum Ordnen der Werte. Diese zweite Form wird nicht von allen Compilern unterstützt. Wenn die zweite Form gewünscht, aber nicht vom Compiler unterstützt wird, kann der allgemeinere generische Algorithmus verwendet werden, der aber nicht ganz so effizient ist.

// Zusammenführung mit expliziter compare-Funktion

list_eleven.merge(list_six, widgetCompare);

//das Folgende ist ähnlich

list<Widget> list_twelve;

merge (list_eleven.begin(), list_eleven.end(),

list_six.begin(), list_six.end(),

inserter(list_twelve, list_twelve.begin()), widgetCompare);

list_eleven.swap(list_twelve);


Entfernen von Elementen

Ebenso, wie es verschiedene Arten gibt, ein Element in eine Liste einzufügen, gibt es auch verschiedene Möglichkeiten, Werte aus Listen zu entfernen. Die am häufigsten Verwendeten Operationen zum Entfernen eines Wertes sind pop_front() oder pop_back(), die das einzelne Element von Anfang beziehungsweise vom Ende der Liste entfernen. Diese Elementfunktionen entfernen einfach das angegebene Element und geben ihrerseits keinen sinnvollen Wert zurück. Wenn für den Elementtyp ein Destruktor definiert ist, wird er beim Entfernen des Elements aufgerufen. Wenn die Werte von dem Entfernen inspiziert werden sollen, können die Elementfunktionen front() und back()verwendet werden.

Die Operation erase() kann verwendet werden, um einen durch einen Iterator bezeichneten Wert zu entfernen. Beim Datentyp List werden der Argumentiterator sowie alle anderen Iteratoren, die die gleiche Position bezeichnen, nach dem Löschvorgang ungültig, aber Iteratoren, die andere Positionen bezeichnen, bleiben davon unberührt. erase() kann auch verwendet werden, um eine ganze Untersequenz zu entfernen, die durch ein Iteratorpaar bezeichnet wird. Die Werte werden aus der Liste entfernt, beginnend beim Anfangsiterator und endend mit dem Endeiterator, diesen aber nicht einschließend. Das Entfernen von Elementen aus der Mitte einer Liste ist ein effizienter Vorgang - im Gegensatz zum Entfernen von Elementen aus der Mitte eines Vektors oder eines Deques.

list_nine.erase (location);

// Werte zwischen der ersten 5 und der folgenden 7 löschen

location = find(list_nine.begin(), list_nine.end(), 5);

list<int>::iterator location2 =

find(location, list_nine.end(), 7);

list_nine.erase (location, location2);

Die Elementfunktion remove() entfernt alle Instanzen eines gegebenen Wertes aus einer Liste. Eine Spielart davon, remove_if(), entfernt alle Werte, die einem gegebenen Prädikat genügen. Eine Alternative zu diesen Elementfunktionen stellen die generischen Algorithmen remove() und remove_if() dar (Entfernen unerwünschter Elemente). Die generischen Algorithmen verringern die Größe der Liste nicht, statt dessen schieben sie die zu behaltenden Elemente an den Anfang der Liste, lassen den Rest der Liste unverändert und geben einen Iterator auf das erste nicht veränderte Element zurück. Dieser Wert kann dann mit der Elementfunktion erase() verwendet werden, um die restlichen Elemente zu entfernen.

list_nine.remove(4); 	// alle Vieren entfernen

list_nine.remove_if(divisibleByThree); 	//alle durch 3 teilbaren Elemente entfernen

// diese Form ist äquivalent

list<int>::iterator location3 =

remove_if(list_nine.begin(), list_nine.end(),

divisibleByThree);

list_nine.erase(location3, list_nine.end());

Die Operation unique() löscht alle außer dem ersten Element aus jeder aufeinanderfolgenden Gruppe gleicher Elemente in einer Liste. Die Liste braucht nicht geordnet zu sein. Eine alternative Funktion verwendet eine binäre Funktion und vergleicht damit nebeneinanderliegende Elemente. Sie entfernt dann den zweiten Wert, wenn die Funktion den Wert true zurückliefert. Wie bei remove_if() wird diese zweite Form von unique() nicht von allen Compilern unterstützt. In diesem Fall kann der allgemeinere generische Algorithmus unique() verwendet werden(siehe dazu auch Entfernen von Gruppen gleicher Werte). In dem folgenden Beispiel ist die verwendete binäre Funktion der Größer-als-Operator. Die Wirkung besteht dann im Entfernen aller Elemente, die kleiner als ein vorangegangenes Element sind.

// erstes von aufeinanderfolgenden gleichen Elementen entfernen

list_nine.unique();

// Vergleichsfunktion explizit angeben

list_nine.unique(greater<int>());

// dies ist dem vorigen Beispiel äquivalent

location3 =

unique(list_nine.begin(), list_nine.end(), greater<int>());

list_nine.erase(location3, list_nine.end());


Umfangsoperationen

Die Elementfunktion size() liefert die Anzahl der von einem Container gehaltenen Elemente zurück. Die Funktion empty() gibt true zurück, wenn der Container leer ist; sie ist effektiver als ein Vergleich der Größe mit dem Wert null.

cout << "Number of elements: " << list_nine.size () << endl;

if ( list_nine.empty () )

cout << "list is empty " << endl;

else

cout << "list is not empty " << endl;

Die Elementfunktion resize() ändert die Größe der Liste auf den im Argument angegebenen Wert. Je nach Bedarf werden am Ende der Kollektion Werte hinzugefügt oder gelöscht. Ein optionales zweites Argument kann verwendet werden, um den Anfangswert für neue Elemente zu liefern, die der Kollektion hinzugefügt werden.

// Größe 12 annehmen, Werte von 17 einsetzen, falls erforderlich

list_nine.resize (12, 17);


Zugriff und Iteration

Die Elementfunktionen front() und back() liefern das erste beziehungsweise das letzte Element im Container zurück, entfernen es jedoch nicht. Bei einer Liste ist der Zugriff auf andere Elemente nur durch Entfernen von Elementen (bis das gewünschte Element zum ersten oder letzten Element wird) oder durch die Verwendung von Iteratoren möglich.

Es gibt zwei Arten von Iteratoren, die für Listen konstruiert werden können. Die Funktionen begin() und end() erzeugen Iteratoren, die die Liste in Vorwärtsrichtung durchschreiten. Für den Datentyp Liste erzeugen begin() und end() bidirektionale Iteratoren. Die alternativen Funktionen rbegin() und rend() konstruieren Iteratoren, die in umgekehrte Reihenfolge fortschreiten, also vom Ende der Liste zum Anfang.


Liste: Testen auf enthaltene Elemente
Liste: Testen auf Inklusion

Der Datentyp List stellt kein Verfahren zur Verfügung, das direkt verwendet werden kann, um festzustellen, ob sich ein bestimmter Wert in der Kollektion befindet. Für diesen Zweck können aber die generischen Algorithmen find() oder count() (Suchen eines Elements, das einer Bedingung genügt und Zählen der Elemente, die einer Bedingung genügen) verwendet werden. Beispielsweise prüfen die folgenden Anweisungen, ob eine Liste mit Integerwerten das Element 17 enthält.

int num = 0;

count(list_five.begin(), list_five.end(), 17, num);

if (num > 0)

cout << "contains a 17" << endl;

else

cout << "does not contain a 17" << endl;

if (find(list_five.begin(), list_five.end(), 17) != list_five.end())

cout << "contains a 17" << endl;

else

cout << "does not contain a 17" << endl;


Sortieren und Operationen für sortierte Listen

Die Elementfunktion sort() ordnet Elemente in aufsteigender Reihenfolge an. Wenn ein anderer Vergleichsoperator als < gewünscht wird, kann er als Argument angegeben werden.

list_ten.sort ( );	// Elemente in Folge anordnen

list_twelve.sort (widgetCompare); 	// Sortieren mit der Elementfunktion widget compare

Nach dem Sortieren kann eine Reihe der generischen Algorithmen für geordnete Kollektionen auf diese Listen angewandt werden. Diese sind ausführlich in Überblick über Algorithmen für geordnete Kollektionen beschrieben.


Suchoperationen

Die verschiedenen Arten von Suchoperationen, die unter Suchoperationen beschrieben sind, nämlich find(), find_if(), adjacent find(), mismatch(), max_element(), min_element() oder search() können auf Listen angewandt werden. In allen Fällen ist das Ergebnis ein Iterator, der dereferenziert werden kann, um das bezeichnete Element zu erhalten, oder der in einer anschließenden Operation verwendet werden kann.

Hinweis:	Die Suchalgorithmen der Standardbibliothek liefern immer den Bereichsendeiterator zurück, wenn kein Element gefunden wurde, das der Suchbedingung entspricht. Wenn die Gültigkeit der Ergebnisses nicht garantiert ist, sollte generell auf die Bereichsende-Bedingung geprüft werden.


Stationäre Transformationen

Eine Reihe von Operationen kann auf Listen angewandt werden, um sie stationär zu transformieren. Einige dieser Operationen stehen als Elementfunktionen zur Verfügung. Andere verwenden einige der generischen Algorithmen, die unter Überblick über generische Algorithmen beschrieben sind.

Bei einer Liste kehrt die Elementfunktion reverse() die Reihenfolge der Elemente in der Liste um.

list_ten.reverse(); 	// die Elemente stehen nun in umgekehrter Reihenfolge

Mit dem generischen Algorithmus transform() (Transformieren von einer oder von zwei Sequenzen) kann jeder Wert in einem Container verändert werden, indem einfach der gleiche Container als Eingabe und als Ergebnis der Operation verwendet wird. Das folgende Quelltextbeispiel erhöht jedes Element einer Liste um eins. Um die benötigte unäre Funktion zu konstruieren, wird das erste Argument der binären Integer-Additionsfunktion an den Wert eins gebunden. Die Version von transform(), die zwei parallele Sequenzen bearbeitet, kann in ähnlicher Weise verwendet werden.

transform(list_ten.begin(), list_ten.end(),

list_ten.begin(), bind1st(plus<int>(), 1));

In analoger Weise können die Funktionen replace() und replace_if() (Ersetzen bestimmter Elemente durch einen festen Wert) verwendet werden, um Elemente in einer Liste durch einen bestimmten Wert zu ersetzen. Auch Rotationen (Rotieren von Elementen um einen Mittelpunkt) und Partitionierung (Aufteilen einer Sequenz in zwei Gruppen) können an Listen vorgenommen werden.

// Position des Wertes 5 suchen und um ihn rotieren

location = find(list_ten.begin(), list_ten.end(), 5);

rotate(list_ten.begin(), location, list_ten.end());

// nun Aufteilen und dazu Werte größer als 7 verwenden

partition(list_ten.begin(), list_ten.end(),

bind2nd(greater<int>(), 7));

Die Funktionen next_permutation() und prev_permutation() (Erzeugen von Permutationen in Folge) können verwendet werden, um die nächste (oder vorangegangene) Permutation einer Kollektion von Werten zu generieren.

next_permutation (list_ten.begin(), list_ten.end());


Andere Operationen

Der Algorithmus for_each() (Anwenden einer Funktion auf alle Elemente in einer Kollektion) wendet eine Funktion auf jedes Element einer Kollektion an. Ein Beispiel für diese Verwendungsweise wird in dem Beispielprogramm zur Wurzelsortierung in dem Abschnitt zur Datenstruktur Deque gegeben.

Der generische Algorithmus accumulate() reduziert eine Kollektion auf einen Skalarwert (siehe dazu auch Reduzieren einer Sequenz auf einen Einzelwert). Dies läßt sich beispielsweise verwenden, um die Summe einer Liste mit Zahlen zu errechnen. Eine weniger offensichtliche Verwendungsweise von accumulate() wird in dem Beispiel zur Wurzelsortierung gezeigt.

cout << "Sum of list is: " <<

accumulate(list_ten.begin(), list_ten.end(), 0) << endl;

Zwei Listen können miteinander verglichen werden. Sie sind dann gleich, wenn sie die gleiche Größe haben und alle entsprechenden Elemente gleich sind. Eine Liste ist dann kleiner als eine andere, wenn sie im lexikographischen Sinne kleiner ist (siehe dazu auch Lexikalischer Vergleich).


Beispielprogramm: ein Inventarsystem

Hinweis:	Die ausführbare Version des Widget Works-Programms befindet sich in der Datei widwork.cpp auf dem mitgelieferten Datenträger.

Wir werden ein einfaches System zur Inventarverwaltung verwenden, um den Gebrauch verschiedener Listenoperationen daran zu erläutern. Dazu nehmen wir ein Unternehmen an, WorldWideWidgetWorks, das ein Programm für die Verwaltung des Widget-Bestandes benötigt. Widgets sind einfache Vorrichtungen, die durch verschiedene Kennummern unterschieden werden:

class  Widget {

public:

Widget(int a = 0) : id(a) { }

void operator = (const Widget& rhs) { id = rhs.id; }

int id;

friend ostream & operator << (ostream & out,const Widget & w)

{ return out << "Widget " << w.id; }

friend bool operator == (const Widget&lhs, const Widget& rhs)

{ return lhs.id == rhs.id; }

friend bool operator< (const Widget&llhs, const Widget& rhs)

{ return lhs.id < rhs.id; }

};

Der Inventarstatus wird durch zwei Listen repräsentiert. Die eine Liste gibt den Lagerbestand verfügbarer Widgets wieder, die zweite gibt die Widget-Typen an, die von Kunden nachbestellt wurden. Die erste ist eine Liste von Widgets, während die zweite Liste Widget-Kenntypen darstellt. Zur Bearbeitung unseres Inventars stehen zwei Befehle zur Verfügung; der erste, order(), bearbeitet Aufträge, während der zweite, receive(), den Versand eines neuen Widgets steuert.

class inventory {

public:

void order (int wid);	// Auftrag für Widget-Typ wid bearbeiten

void receive (int wid);	// Widget vom Typ wid im Versand entgegennehmen

private:

list<Widget> on_hand;

list<int> on_order;

};

Wenn ein neues Widget in der Versandabteilung eintrifft, vergleichen wir die Widget-Kennummer mit der Liste der Widget-Typen auf der Nachbestellung. Wir verwenden find(), um die Liste der Nachbestellungen zu durchsuchen, und verschicken das Widget sofort, wenn es erforderlich ist. Andernfalls nehmen wir es in den Lagerbestand auf.

void inventory::receive (int wid)

{

cout << "Received shipment of widget type " << wid << endl;

list<int>::iterator weneed =

find (on_order.begin(), on_order.end(), wid);

if (weneed != on_order.end()) {

cout << "Ship " << Widget(wid)

<< " to fill back order" << endl;

on_order.erase(weneed);

}

else

on_hand.push_front(Widget(wid));

}

Wenn ein Kunde ein neues Widget bestellt, kontrollieren wir die Liste der am Lager befindlichen Widgets um herauszufinden, ob der Auftrag sofort bearbeitet werden kann. Wir können zum Durchsuchen der Liste die Funktion find_if() verwenden. Wir benötigen zu diesem Zweck eine binäre Funktion, die als Argument ein Widget annimmt und feststellt, ob das Widget dem verlangten Typ angehört. Das läßt sich erreichen, indem wir in einer allgemeinen binären Widget-Testfunktion das zweite Argument an den bestimmten Widget-Typ binden. Damit die Funktion bind2nd()verwendet werden kann, muß die binäre Funktion eine Instanz der Klasse binary_function sein. Die allgemeine Widget-Testfunktion ist wie folgt implementiert:

class WidgetTester : public binary_function<Widget, int, bool> {

public:

bool operator () (const Widget & wid, int testid) const

{ return wid.id == testid; }

};

Die Widget-Auftrags-Funktion ist dann so implementiert:

void inventory::order (int wid)

{

cout << "Received order for widget type " << wid << endl;

list<Widget>::iterator wehave =

find_if (on_hand.begin(), on_hand.end(),

bind2nd(WidgetTester(), wid));

if (wehave != on_hand.end()) {

cout << "Ship " << *wehave << endl;

on_hand.erase(wehave);

}

else {

cout << "Back order widget of type "  << wid  << endl;

on_order.push_front(wid);

}

}


Deque

7.1 Die Datenabstraktion Deque

7.2 Deque-Operationen

7.3 Beispielprogramm -- Wurzelsortierung


Die Datenabstraktion Deque

Der Name "Deque" ist eine Kurzform für "double-ended queue" und wird "Deck" ausgesprochen. In der traditionellen Verwendung wird dieser Ausdruck benutzt, um jede Datenstruktur zu bezeichnen, die Einfüge- und Löschvorgänge sowohl am Anfang als auch am Ende einer Kollektion gestattet. Die Container-Klasse Deque erlaubt dies und auch noch einiges mehr. In der Tat stellen die Fähigkeiten der Datenstruktur Deque fast die Vereinigungsmenge der Fähigkeiten der Klassen Vector und List dar.

Wie ein Vektor ist ein Deque eine indizierte Kollektion. Auf Werte kann über einen Index zugegriffen werden, dabei dient die Position in der Kollektion als Schlüssel (eine Fähigkeit, die in der Klasse List nicht zur Verfügung steht).

Wie bei einer Liste lassen sich bei einem Deque Werte effizient entweder am Anfang oder am Ende einfügen (eine Fähigkeit, die von der Klasse Vector nur zum Teil geboten wird).

Wie bei den Klassen List und Vector können Einfügungen auch in der Mitte der in einem Deque enthaltenen Sequenz vorgenommen werden. Solche Einfügeoperationen sind nicht so effizient wie bei einer Liste, aber doch etwas effizienter als bei einem Vektor.

Kurz gesagt kann ein Deque häufig ebenso in Fällen verwendet werden, die einen Vektor erfordern, als auch in solchen, für die sich eine Liste anbietet. Oftmals führt die Entscheidung für ein Deque anstelle der Alternative Vektor/Liste zu schnellerer Programmausführung. Um festzustellen, welche Datenstruktur Sie verwenden sollten, können Sie die Fragensammlung unter Wahl des geeigneten Containers zu Rate ziehen.


Deque-Include-Dateien

Die Header-Datei deque muß in alle Programme aufgenommen werden, die die Datenstruktur Deque verwenden.

# include <deque>


Deque-Operationen

Die folgende Tabelle faßt die Elementfunktionen zusammen, die im Datentyp Deque zur Verfügung stehen. Dabei fällt die große Ähnlichkeit dieser Tabelle mit den Tabellen für die Datenstrukturen Vector und List auf. Zu den Operationen, die den zuvor besprochenen Elementfunktionen für die Klassen Vector und List entsprechen, werden keine weiteren Erläuterungen gegeben.

Ergebnis	Name	Argumente

	deque	()
	deque	(size_type)
	deque	(size_type, value_type)
	deque	template <class Iterator> (Iterator, Iterator)
	deque	template <class Iterator> assign (Iterator, Iterator)
	deque	template <class Size,class T> assign (Size, T)
	deque	(const deque)
reference	at	(size_type)
reference	back	()
RandomAccessIterator	begin	()
bool	empty	()
RandomAccessIterator	end	()
void	erase	(iterator)
void	erase	(iterator,iterator)
reference	front	()
iterator	insert	(iterator, value_type)
void	insert	(iterator, size_type, value_type)
void	insert	template <class Iterator> (iterator, Iterator, Iterator)
size_type	max_size	()
void	pop_back	()
void	pop_front	()
void	push_back	(value_type)
void	push_front	(value_type)
RandomAccessIterator	rbegin	()
RandomAccessIterator	rend	()
void	resize	(size_type, value_type)
size_type	size	()
void	swap	(deque)
reference	operator []	size_type
deque	operator =	deque
Ein Deque wird in der gleichen Weise wie ein Vektor deklariert und enthält in der Klasse die gleichen Typdefinitionen wie Vector.

Beachten Sie, daß die Elementfunktionen begin() und end() Random Access-Iteratoren zurückliefern, anstelle der bidirektionalen Iteratoren, die bei Listen zurückgegeben werden.

Eine Einfügung (entweder insert(), push_front() oder push_back()) kann unter Umständen alle noch ausstehenden Iteratoren und Referenzen auf Elemente in dem Deque ungültig werden lassen. Wie beim Datentyp Vector bedeutet dies eine wesentlich strengere Einschränkung als bei Einfügungen in eine Liste.

Wenn der zugrundeliegende Elementtyp einen Destruktor zur Verfügung stellt, wird dieser aufgerufen, wenn ein Wert aus einem Deque gelöscht wird.

Da der Datentyp Deque Random Access-Iteratoren zur Verfügung stellt, können alle generischen Algorithmen, die mit Vektoren eingesetzt werden können, auch für Deques verwendet werden.

Ein Vektor hält die Elemente in einem einzigen großen Speicherblock. Ein Deque verwendet demgegenüber eine Reihe kleinerer Blöcke. Dieser Unterschied kann bei Systemen, die die Größe von Speicherblöcken beschränken, wichtig sein, da auf solchen Systemen ein Deque erheblich mehr Elemente enthalten kann als ein Vektor.

Wenn Werte eingefügt werden, ändern sich die Indexwerte für die einzelnen Elemente in der Kollektion. Wenn beispielsweise ein Wert an Position 3 eingefügt wird, befindet sich der Wert, der früher an Indexposition 3 stand nun an Indexposition 4, der Wert von Position 4 befindet sich dann an Position 5 usw.


Beispielprogramm: Wurzelsortierung

Der Algorithmus zur Wurzelsortierung ist ein gutes Beispiel, wie sich Listen und Deques mit anderen Containern kombinieren lassen. Im Fall der Wurzelsortierung wird ein Vektor aus Deques bearbeitet, ganz ähnlich wie in einer Hash-Tabelle.

Hinweis:	Das vollständige Programm Wurzelsortierung befindet sich in der Datei radix.cpp auf dem mitgelieferten Datenträger.

Wurzelsortierung ist ein Verfahren für das Ordnen einer Liste positiver Integerwerte. Die Werte werden der Reihe nach auf Zehnerstellen geordnet, von rechts nach links. Dazu werden die Werte zunächst in "Eimer" kopiert; der Index für diesen Eimer ergibt sich aus der Stelle, die sortiert wird. Sobald alle Stellen untersucht worden sind, muß die Liste sortiert sein.

Die folgende Tabelle zeigt die Sequenzen der Werte in den vier Eimern während der vier Schritte, die das Sortieren der Liste 624 852 426 987 269 146 415 301 730 78 593 umfaßt. Während des ersten Durchlaufs werden die Einerstellen sortiert. Im zweiten Schritt werden die Zehnerstellen sortiert, dabei wird die relative Position der Werte, die sich im ersten Durchgang ergeben hat, beibehalten. In Durchgang drei werden die Hunderterstellen sortiert, und es wird wieder die relative Reihenfolge beibehalten. Nach drei Durchgängen ergibt sich eine geordnete Liste.

Eimer 	 1. Durchgang 	 2. Durchgang 	 3. Durchgang

0 	 730 	 301 	   78

1 	 301 	 415 	 146

2 	 852 	 624, 426 	 269

3 	 593 	 730 	 301

4 	 624 	 146 	 415, 426

5 	 415 	 852 	 593

6 	 426, 146 	 269 	 624

7 	 987 	   78 	 730

8 	   78 	 987 	 852

9 	 269 	 593 	 987

Der Algorithmus für die Wurzelsortierung ist einfach. Eine while-Schleife arbeitet die einzelnen Durchgänge ab. Der Wert der Variablen divisor gibt an, welche Stelle aktuell untersucht wird. Ein boolesches Flag wird eingesetzt, um festzustellen, wann die Ausführung enden soll. Bei jeder Ausführung der while-Schleife wird ein Vektor von Deques deklariert. Indem die Deklaration dieser Struktur in das Innere der while-Schleife gelegt wird, wird sie für jeden Durchgang neu als leer initialisiert. Bei jeder Ausführung der Schleife werden die Werte in der Liste in die passenden Eimer kopiert, indem die Funktion copyIntoBuckets() für jeden Wert ausgeführt wird. Nachdem die Werte auf die Eimer verteilt sind, werden sie wieder in der Liste versammelt; dazu wird eine Akkumulation benutzt.

void radixSort(list<unsigned int> & values)

{

bool flag = true;

int divisor = 1;

while (flag) {

vector< deque<unsigned int> > buckets(10);

flag = false;

for_each(values.begin(), values.end(),

copyIntoBuckets(...));

accumulate(buckets.begin(), buckets.end(),

values.begin(), listCopy);

divisor *= 10;

}

}

Die Verwendung der Funktion accumulate() in diesem Beispiel weicht vom Üblichen ab. Der "skalare" Wert, der hier konstruiert wird, ist nämlich die Liste selbst. Der Initialwert für die Akkumulation ist der Iterator, der den Anfang der Liste bezeichnet. Jeder Eimer wird von folgender binärer Funktion verarbeitet:

list<unsigned int>::iterator

listCopy(list<unsigned int>::iterator c,

deque<unsigned int> & lst)

{

// Liste in Originalliste zurückkopieren, Ende zurückliefern

return copy(lst.begin(), lst.end(), c);

}

Die einzige noch verbleibende Schwierigkeit ist die Definition der Funktion copyIntoBuckets(). Das Problem besteht darin, daß die Funktion als Argument nur das einzufügende Element annehmen darf, sie aber zugleich Zugriff auf die drei Werte buckets, divisor und flag haben muß. In Sprachen, die die Definition von Funktionen innerhalb anderer Funktionen gestatten, bestände die Lösung in der Definition von copyIntoBuckets() als lokaler Funktion innerhalb der while-Schleife. C++ verfügt aber nicht über diese Fähigkeit. Statt dessen müssen wir eine Klassendefinition erzeugen, die dann mit Referenzen auf die entsprechenden Werte initialisiert werden kann. Der Klammeroperator (Funktionsaufruf-Operator) für diese Klasse wird dann als Funktion für den Aufruf von for_each() im Programm zur Wurzelsortierung verwendet.

class copyIntoBuckets {

public:

copyIntoBuckets

(int d, vector< deque<unsigned int> > & b, bool & f)

: divisor(d), buckets(b), flag(f) {}

int divisor;

vector<deque<unsigned int> > & buckets;

bool & flag;

void operator () (unsigned int v)

{	int index = (v / divisor) % 10;

// flag wird auf true gesetzt, wenn ein

// Eimer als der nullte verwendet wird

if (index) flag = true;

buckets[index].push_back(v);

}

};


Set und Multiset

8.1 Die Datenabstraktion Set

8.2 Set- und Multiset-Operationen

8.3 Beispielprogramm - Rechtschreibprüfung

8.4 Die Klasse bit_set


Die Datenabstraktion Set

Hinweis:	Zwar impliziert das abstrakte Konzept des Sets nicht notwendig eine geordnete Kollektion, trotzdem ist der Datentyp Set immer geordnet. Falls erforderlich kann eine Kollektion von Werten, die nicht geordnet werden kann, in einem anderen Datentyp, beispielsweise einer Liste, gehalten werden.

Ein Set ist eine Kollektion von Werten. Da der Container, der für die Implementierung der Datenstruktur Set verwendet wird, die Werte in einer geordneten Darstellung enthält, sind Sets für das Einfügen und Entfernen von Elementen sowie für den Test auf Inklusion von Werten optimiert. Jede dieser Operationen kann in einer logarithmischen Anzahl von Schritten ausgeführt werden, während bei einer Liste, einem Vektor oder einem Deque im ungünstigsten Fall eine Überprüfung aller im Container enthaltenen Werte erforderlich ist. Aus diesem Grund sollten Sets die bevorzugte Datenstruktur für alle Fälle sein, in denen das Einfügen oder Entfernen von Werten und die Prüfung auf Inklusion eine Hauptrolle spielen. Ebenso wie eine Liste ist ein Set nicht in der Größe beschränkt; vielmehr vergrößert und verkleinert es sich, wenn Elemente der Kollektion hinzugefügt oder aus ihr entfernt werden.

In der Standardbibliothek stehen zwei Arten von Sets zur Verfügung. In dem Container Set kann jedes Element nur einmal enthalten sein. Versuche, einen Wert einzufügen, der bereits im Set enthalten ist, werden ignoriert. In dem Container Multiset sind dagegen mehrere Instanzen des gleichen Wertes zulässig.


Set-Include-Dateien

Wenn Sie die Datentypen Set oder Multiset verwenden, müssen Sie die Header-Datei set in Ihr Programm aufnehmen.

# include <set>


Set- und Multiset-Operationen

Hinweis:	In anderen Programmiersprachen wird ein Multiset manchmal als bag bezeichnet.

Die folgende Tabelle faßt die Elementfunktionen zusammen, die von den Datentypen Set und Multiset zur Verfügung gestellt werden. Jeder von ihnen wird weiter unten ausführlicher behandelt. Beachten Sie, daß die Elementfunktionen zwar die Grundfunktionen zur Verfügung stellen, die Nützlichkeit dieser Datenstrukturen aber durch die Verwendung der generischen Algorithmen, die unter Überblick über generische Algorithmen und Überblick über Algorithmen für geordnete Kollektionen beschrieben sind, erheblich gesteigert wird.

Ergebnis	Name	Argumente

	set	()
	multiset	()
	set	(Compare)
	multiset	(Compare)
	set	template <class Iterator> (Iterator, Iterator)
	multiset	template <class Iterator> (Iterator, Iterator)
	set	(const set)
	multiset	(const multiset)
BidirectionalIterator	begin	()
size_type	count	(value_type)
bool	empty	()
BidirectionalIterator	end	()
pair<iterator, iterator>	equal_range	(value_type)
void	erase	(iterator)
size_type	erase	(value_type)
void	erase	(iterator, iterator)
iterator	find	(value_type)
pair<iterator, bool>	insert	(value_type)
iterator	insert	(iterator, value_type)
void	insert	template <class Iterator> (Iterator, Iterator)
iterator	lower_bound	(value_type)
size_type	max_size	()
BidirectionalIterator	rbegin	()
BidirectionalIterator	rend	()
size_type	size	()
void	swap	(set)
iterator	upper_bound	(key_type)
Function	value_comp	()
set	operator =	(set)


Erzeugung und Initialisierung

Ein Set ist eine Template-Datenstruktur, die durch den Typ der Elemente, die sie enthält und durch den Operator, der für den Vergleich der Schlüssel verwendet wird, spezialisiert wird. Dieses zweite Argument ist optional, und wenn es fortgelassen wird, wird von dem Kleiner-als-Operator für den Schlüsseltyp ausgegangen. Der Elementtyp kann ein primitiver Sprachtyp (wie etwa integer oder double) sein, ein Zeigertyp oder ein benutzerdefinierter Typ. Der Elementtyp muß sowohl den Gleichheitsoperator (Operator ==) als auch den Kleiner-Als-Vergleichsoperator (Operator <) kennen.

Hinweis:	Wie schon in den vorangegangenen Ausführungen zu Vektoren und Listen ausgeführt, erfordert die Initialisierung von Containern mittels eines Iteratorpaars ein Verfahren, das von Compilern noch nicht in breitem Maßstab unterstützt wird. Steht keine Unterstützung durch den Compiler zur Verfügung, kann die gleiche Wirkung durch das Deklarieren eines leeren Sets und anschließendes Kopieren von Werten in dieses Set mittels des generischen Algorithmus copy() erreicht werden.

Sets können ohne Initialwerte deklariert werden, oder sie können von einem anderen Container initialisiert werden, indem ein Iteratorenpaar zur Verfügung gestellt wird. In beiden Fällen gibt es das optionale Argument einer alternativen Vergleichsfunktion; dieser Wert überschreibt den im Template-Parameter gesetzten Wert. Dieses Verfahren ist dann nützlich, wenn ein Programm zwei oder mehr Sets mit den gleichen Werten aber unterschiedlicher Ordnung enthält, da es verhindert, daß mehr als ein Exemplar der Elementfunktion Set instantiiert wird. Mit dem Kopierkonstruktor kann ein Clone oder eine Kopie eines Sets erstellt werden.

set <int> set_one;

set <int, greater<int> > set_two;

set <int> set_three(greater<int>());

set <gadget, less<gadget>() > gset;

set <gadget> gset(less<gadget>())

set <int> set_four (aList.begin(), aList.end());

set <int> set_five

(aList.begin(), aList.end(), greater<int>() );

set <int> set_six (set_four);	// Kopierkonstruktor

Ein Set kann einem anderen Set zugewiesen werden, und zwei Sets können ihre Werte mittels der Operation swap() tauschen (analog dem Verfahren bei den anderen Containern der Standardbibliothek).

set_one = set_five;

set_six.swap(set_two);


Typdefinitionen

Die Klassen Set und Multiset enthalten eine Reihe von Typdefinitionen. Meistens werden diese in einer Deklarationsanweisung verwendet. Beispielsweise kann ein Iterator für ein Integerset auf folgende Weise deklariert werden:

set<int>::iterator location;

Außer iterator sind die folgenden Typen definiert:

value_type	Der Typ der im Set enthaltenen Elemente.
const_iterator	Ein Iterator, der keine Veränderung der zugrundeliegenden Sequenz erlaubt.
reverse_iterator	Ein Iterator, der in umgekehrter Richtung fortschreitet.
const_reverse_iterator	Eine Kombination aus konstantem und Reverse-Iterator.
reference	Eine Referenz auf ein zugrundeliegendes Element.
const_reference	Eine Referenz auf ein zugrundeliegendes Element, die keine Veränderung gestattet.
size_type	Ein vorzeichenloser Integertyp, der die Größe von Containern angibt.
value_compare	Eine Funktion, die für den Vergleich zweier Elemente verwendet werden kann.
difference_type	Ein Integertyp mit Vorzeichen, der die Distanz zwischen Iteratoren angibt.


Einfügung

Hinweis:	Siehe auch die Ausführungen zur Datenstruktur Map in Die Datenabstraktion Map, wenn Sie die Beschreibung des Datentyps pair benötigen.

Anders als bei einer Liste oder einem Vektor, kann ein neues Element nur auf eine Art in ein Set eingefügt werden. Ein Wert kann in einen Set oder einen Multiset nur mit der Elementfunktion insert() eingefügt werden. Bei einem Multiset liefert die Funktion einen Iterator zurück, der den soeben eingefügten Wert bezeichnet. Einfügeoperationen in einen Set liefern ein Wertepaar zurück, in dem das erste Feld einen Iterator enthält und das zweite einen booleschen Wert, der true ist, wenn das Element eingefügt wurde, andernfalls false. Beachten Sie, daß ein Element nicht in einen Set eingefügt wird, wenn es mit einem darin bereits enthaltenen Element übereinstimmt.

set_one.insert (18);

if (set_one.insert(18).second)

cout << "element was inserted" << endl;

else

cout << "element was not inserted " << endl;

Auch Einfügungen mehrerer Elemente zugleich aus einem anderen Container können durchgeführt werden. Dazu wird ein Iteratorenpaar verwendet:

set_one.insert (set_three.begin(), set_three.end());


Entfernen von Elementen aus einer Menge
Entfernen von Elementen aus einem Set

Werte werden aus einem Set mit der Elementfunktion erase() entfernt. Das Argument kann entweder ein bestimmter Wert sein, ein Iterator, der einen einzelnen Wert bezeichnet, oder ein Iteratorpaar, die einen Wertebereich bezeichnen. Wenn die erste Form bei einem Multiset verwendet wird, werden alle Elemente, die mit dem Argumentwert übereinstimmen, entfernt, und der Rückgabewert gibt die Anzahl der entfernten Elemente an.

// Element, das gleich 4 ist, entfernen

set_three.erase(4);

// Element fünf entfernen

stesttype::iterator five = set_three.find(5);

set_three.erase(five);

// alle Werte zwischen sieben und elf entfernen

stesttype::iterator seven = set_three.find(7);

stesttype::iterator eleven = set_three.find(11);

set_three.erase (seven, eleven);

Wenn der zugrundeliegende Elementtyp einen Destruktor zur Verfügung stellt, wird der Destruktor vor dem Entfernen der Elemente aus der Kollektion aufgerufen.


Suchen und Zählen

Die Elementfunktion size() gibt die Anzahl der Elemente zurück, die von einem Container gehalten werden. Die Elementfunktion empty() liefert einen booleschen Wert true zurück, wenn der Container leer ist, und ist im allgemeinen schneller als das Vergleichen der Größe mit null.

Die Elementfunktion find() nimmt einen Elementwert an und gibt einen Iterator zurück, der die Position des Wertes im Set bezeichnet, wenn der Wert im Set enthalten ist. Andernfalls wird ein Wert zurückgegeben, der dem Ende des Sets entspricht (dem Rückgabewert der Funktion end()). Wenn ein Multiset mehr als ein passendes Element enthält, kann der Rückgabewert ein beliebiger dieser entsprechenden Werte sein.

list<int>::iterator five = set_three.find(5);

if (five != set_three.end())

cout << "set contains a five" << endl;

Die Elementfunktionen lower_bound() und upper_bound() sind vor allem bei multisets sinnvoll; bei der Verwendung mit sets ahmen sie einfach die Funktion function find() nach. Die Elementfunktion lower_bound() ergibt den ersten Eintrag, der dem Argumentschlüssel entspricht, während die Elementfunktion upper_bound() den ersten Wert nach dem letzten Eintrag, der dem Argument entspricht, zurückliefert. Schließlich gibt die Elementfunktion equal_range() ein Iteratorenpaar zurück, das die Unter- und Obergrenzen (lower and upper bounds) enthält.

Die Elementfunktion count() gibt die Anzahl der Elemente zurück, die mit dem Argument übereinstimmen. Bei einem Set ist dieser Wert entweder null oder eins, während es bei einem Multiset jeder beliebige nicht negative Wert sein kann Da ein Integerwert ungleich null als true behandelt wird, kann die Funktion count() für den Test auf Inklusion eines Elements verwendet werden, wenn es nur darum geht herauszufinden, ob ein Element in einem Set enthalten ist oder nicht. Die Alternative, die Verwendung von find(), macht einen Vergleich des Ergebnisses von find() mit dem Kollektions-Endeiterator erforderlich.

if (set_three.count(5))

cout << "set contains a five" << endl;


Iteratoren

Hinweis:	Im Gegensatz zu einem Vektor oder einem Deque werden Iteratoren oder Referenzen auf andere Elemente in der Kollektion durch das Einfügen oder Entfernen von Elementen in ein Set bzw. aus einem Set nicht ungültig.

Die Elementfunktionen begin() und end() liefern Iteratoren sowohl für sets als auch für multisets. Die Iteratoren, die von diesen Funktionen erzeugt werden, sind konstant, um sicherzustellen, daß die Ordnung des Sets nicht versehentlich oder absichtlich zerstört wird, etwa indem einem Element des Sets ein neuer Wert zugewiesen wird. Elemente werden von den Iteratoren nacheinander erzeugt und von dem Vergleichsoperator geordnet, der beim Deklarieren des Sets zur Verfügung gestellt wurde. Die Elementfunktionen rbegin() und rend() erzeugen Iteratoren, die die Elemente in umgekehrter Reihenfolge liefern.


Mengenoperationen

Die traditionellen Mengenoperationen Teilmengentest, Vereinigungsmenge, Schnittmenge und Differenzmenge stehen nicht als Elementfunktionen zur Verfügung. Sie sind vielmehr als generische Algorithmen implementiert, die für alle geordneten Strukturen funktionieren. Diese Funktionen sind ausführlicher unter Mengenoperationen beschrieben. Die folgende Zusammenfassung beschreibt, wie diese Funktionen zusammen mit den Container-Klassen Set und Multiset verwendet werden können.

Teilmengentest

Die Funktion includes() kann verwendet werden um festzustellen, ob eine Menge eine Teilmenge einer anderen ist, d.h. ob alle Elemente der ersten in der zweiten enthalten sind. Bei multisets muß die Anzahl gleicher Elemente im zweiten Set größer als im ersten sein. Die vier Argumente sind ein Iteratorenpaar, das das (vermutlich) kleinere Set sowie ein Iteratorpaar, das das (möglicherweise) größere Set darstellt.

if (includes(set_one.begin(), set_one.end(),

set_two.begin(), set_two.end()))

cout << "set is a subset" << endl;

Der Kleiner-als-Operator (Operator <) wird für den Vergleich der Elemente verwendet, unabhängig von dem in der Deklaration des Sets verwendeten Operator. Für Fälle, in denen dies unpassend ist, steht eine alternative Version der Funktion includes() zur Verfügung. Diese Form nimmt ein fünftes Argument an, nämlich die Vergleichsfunktion, die zum Ordnen der Elemente in den beiden Sets verwendet wird.

Vereinigungsmenge oder Schnittmenge

Die Funktion set_union() kann verwendet werden, um eine Vereinigungsmenge zweier Sets zu konstruieren. Die zwei Mengen werden durch Iteratorenpaare angegeben, und die Vereinigungsmenge wird in einen Ausgabeiterator kopiert, der als fünftes Argument zur Verfügung steht. Um das Ergebnis als Set zu formieren, muß ein Einfügeiterator verwendet werden, der den Ausgabeiterator bildet. (Siehe dazu die Beschreibung von Einfügeiteratoren unter Einfügeiteratoren.) Wenn das gewünschte Ergebnis eine Vereinigungsmenge einer Menge mit einer anderen ist, kann ein temporŠres Set konstruiert werden, und die Ergebnisse können vor dem Löschen des temporŠres Set mit der Argumentmenge vertauscht werden.

// Vereinigungsmenge zweier Mengen erzeugen, Ergebnis in einen Vektor kopieren

vector<int> v_one (set_one.size() + set_two.size());

set_union(set_one.begin(), set_one.end(),

set_two.begin(), set_two.end(), v_one.begin());

// Vereinigungsmenge an Ort und Stelle bilden

{

set<int> temp_set;

set_union(set_one.begin(), set_one.end(),

set_two.begin(), set_two.end(),

inserter(temp_set, temp_set.begin()));

set_one.swap(temp_set);  // temp_set wird gelöscht

}

Die Funktion set_intersection() ist ähnlich und bildet die Schnittmenge der beiden Mengen.

Ebenso wie bei der Funktion includes() wird der Kleiner-als-Operator (Operator <) verwendet, um die Elemente in den beiden Argumentmengen zu vergleichen, unabhängig von dem Operator, der bei der Deklaration der Sets zur Verfügung gestellt wurde. Sollte dies unpassend sein, erlauben alternative Versionen der beiden Funktionen set_union() und set_intersection() die Angabe des zur Bildung des Sets verwendeten Vergleichsoperators als sechstes Argument.

Die Vereinigungsmenge zweier multisets muß von einer Zusammenführung (merge) der beiden Mengen unterschieden werden. Angenommen, die eine Argumentmenge enthält drei Instanzen des Elements 7, und die zweite enthält zwei Instanzen des gleichen Werts. Dann enthält die Vereinigungsmenge diesen Wert nur dreimal, die aus der Zusammenführung der beiden Mengen entstehende Menge enthält ihn jedoch fünfmal. Zum Zusammenführen der beiden Mengen kann die Funktion merge() verwendet werden (siehe dazu Zusammenführen von geordneten Sequenzen). Die Argumente dieser Funktion entsprechen exakt denen der Funktion set_union().

Differenzmenge

Es gibt zwei Formen der Differenzmenge. Eine einfache Differenzmenge enthält die Elemente der ersten Menge, die nicht in der zweiten enthalten sind. Eine symmetrische Differenzmenge besteht aus der Vereinigung der Elemente aus der ersten Menge, die nicht in der zweiten enthalten sind, mit denen aus der zweiten, die nicht in der ersten Menge enthalten sind. Diese beiden Werte werden von den Funktionen set_difference() und set_symmetric_difference(), zurückgegeben. Die Verwendung dieser beiden Funktionen ist ähnlich wie die der Funktion set_union(), die weiter oben beschrieben wurde.


Andere generische Algorithmen

Da Sets geordnet sind und konstante Iteratoren haben, sind eine Reihe der generischen Funktionen, die unter Überblick über generische Algorithmen und Überblick über Algorithmen für geordnete Kollektionen beschrieben sind, entweder nicht anwendbar oder nicht besonders nützlich. Die folgende Tabelle faßt jedoch einige der Funktionen zusammen, die sinnvoll in Verbindung mit dem Datentyp Set verwendet werden können.

Verwendung	Name

Kopieren einer Sequenz in eine andere Kopieren einer Sequenz in eine andere Sequenz	copy
Suchen eines Elements, das einer Bedingung entspricht Suchen eines Elements, das einer Bedingung genügt	find_if
Suchen einer Untersequenz innerhalb einer Menge Suchen einer Untersequenz innerhalb einer Sequenz	search
Zählen der Elemente, die einer Bedingung genügen Zählen der Elemente, die einer Bedingung genügen	count_if
Reduzieren einer Menge auf einen Einzelwert Reduzieren einer Sequenz auf einen Einzelwert	accumulate
Anwenden einer Funktion auf jedes Element Anwenden einer Funktion auf alle Elemente in einer Kollektion	for_each


Beispielprogramm: Rechtschreibprüfung

Hinweis:	Dieses Programm befindet sich in der Datei spell.cpp auf dem mitgelieferten Datenträger.

Ein einfaches Beispielprogramm, das ein Set verwendet, ist eine Rechschreibprüfung. Das Prüfprogramm nimmt zwei Eingabestreams als Argumente an. Der erste Stream besteht aus korrekt geschriebenen Wörtern (ist also ein Wörterbuch), der zweite ist eine Textdatei. Zuerst wird das Wörterbuch in ein Set eingelesen. Dafür werden ein copy()-Algorithmus und ein Eingabe-Stream-Iterator verwendet, und die Werte werden in einen Einfügeiterator für das Wörterbuch kopiert. Anschließend werden die Wörter aus dem Text eins nach dem anderen überprüft, um festzustellen, ob sie sich im Wörterbuch befinden. Ist das nicht der Fall, werden sie einem Set falsch geschriebener Wörter hinzugefügt. Nachdem der gesamte Text untersucht worden ist, gibt das Programm die Liste der falsch geschriebenen Wörter aus.

void spellCheck (istream & dictionary, istream & text)

{

typedef set <string, less<string> > stringset;

stringset words, misspellings;

string word;

istream_iterator<string, ptrdiff_t> dstream(dictionary), eof;

// zuerst das Wörterbuch lesen

copy (dstream, eof, inserter(words, words.begin()));

// als nächstes den Text lesen

while (text >> word)

if (! words.count(word))

misspellings.insert(word);

// schließlich alle falsch geschriebenen Wörter ausgeben

cout << "Misspelled words:" << endl;

copy (misspellings.begin(), misspellings.end(),

ostream_iterator<string>(cout, "\n"));

}

Eine Verbesserungsmöglichkeit wäre, für jedes falsch geschriebene Wort Alternativen vorzuschlagen. Es gibt verschiedene heuristische Verfahren, die zum Auffinden von Alternativen verwendet werden können. Die hier verwendete Technik besteht im Vertauschen benachbarter Buchstaben. Um diese zu finden, wird ein Aufruf der folgenden Funktion in die Schleife eingefügt, die die falsch geschriebenen Wörter anzeigt.

void findMisspell(stringset & words, string & word)

{

for (int i = 1; i < word.length(); i++) {

swap(word[i-1], word[i]);

if (words.count(word))

cout << "Suggestion: " << word << endl;

// Wort wie zuvor zurückgeben

swap(word[i-1], word[i]);

}

}


Die Klasse bit_set

Ein bit_set stellt gewissermaßen eine Kreuzung zwischen den Typen Set und Vector dar. Wie die Vektorabstraktion vector<bool>, stellt diese Abstraktion ein Set von Binärwerten (0/1 Bit) dar. Jedoch können auf solchen Bit-Mengen Mengenoperationen ausgeführt werden, unter Verwendung der logischen bitweisen Operatoren . Die Klasse bit_set stellt keinerlei Iteratoren für den Zugriff auf Elemente zur Verfügung.

Initialisierung und Erzeugung

Ein bit_set ist eine Template-Klassen-Abstraktion. Das Template-Argument ist jedoch kein Typ, sondern ein Integerwert. Der Wert gibt die Anzahl Bits an, die das Set enthält.

bit_set<126> bset_one;	// eine Menge mit 126 Bits erzeugen

Bei einem alternativen Verfahren besteht die Möglichkeit, die Größe des Sets als Konstruktorargument anzugeben. Die tatsächliche Größe ist dann der kleinere der beiden Werte, die als Template-Argument und als Konstruktor-Argument verwendet werden. Dieses Verfahren ist dann nützlich, wenn ein Programm zwei oder mehr Bit-Vektoren von verschiedener Größe enthält. Wird durchgängig die größere Größe für das Template-Argument verwendet, wird nur ein Methodensatz für die Klasse erzeugt. Die tatsächliche Größe wird dann durch den Konstruktor festgelegt.

bit_set<126> bset_two(100);	// Diese Menge enthält nur 100 Elemente

Eine dritte Konstruktorform nimmt als Argument einen String von 0- und 1-Zeichen an. Es wird ein bit_set erzeugt, das so viele Elemente enthält, wie sich Zeichen in dem String befinden; es wird mit den Werten aus dem String initialisiert.

bit_set<126> small_set("10101010");	// diese Menge hat 8 Elemente

Zugriff und Testen der Elemente

Auf ein einzelnes Bit in dem bit_set kann über die Indizierungsoperation zugegriffen werden. Ob das Bit eins ist oder nicht, läßt sich mit der Elementfunktion test() feststellen. Ob ein beliebiges Bit im Set "gesetzt" ist, wird von der Funktion any(), die einen booleschen Wert zurückgibt, geprüft. Die Umkehrung von any() wird von der Elementfunktion none() zurückgegeben.

bset_one[3] = 1;

if (bset_one.test(4))

cout << "bit position 4 is set" << endl;

if (bset_one.any())

cout << "some bit position is set" << endl;

Mit der Funktion set() kann ein bestimmtes Bit gesetzt werden. bset_one.set(i) ist dem Ausdruck bset_one[i] = true äquivalent. Das Aufrufen der Funktion ohne Argumente setzt alle Bit-Positionen auf true. Die Funktion reset() ist ähnlich, und setzt die angegebene Position auf false (bzw. setzt alle Positionen auf false, wenn sie ohne Argument aufgerufen wird). Die Funktion flip() kehrt entweder die angegebene Position um oder alle, wenn kein Argument angegeben wird. Die Funktion flip() steht auch als Elementfunktion für die einzelnen Bit-Referenzen zur Verfügung.

bset_one.flip();	// gesamte Menge umkehren

bset_one.flip(12);	// nur Bit 12 umkehren

bset_one[12].flip();	// Bit 12 erneut umkehren

Die Elementfunktion size() liefert die Größe des bit_set zurück, während die Elementfunktion count() die Anzahl der gesetzten Bits zurückgibt.

Mengenoperationen

Mengenoperationen an bit_sets werden unter Verwendung der bitweisen Operatoren implementiert; dies geschieht analog zu der Art und Weise, wie die gleichen Operatoren auf Integerargumente wirken.

Der Negationsoperator (Operator ~) liefert bei der Anwendung auf ein bit_set ein neues bit_set zurück, das die Umkehrung der Elemente in der Argumentmenge enthält.

Die Schnittmenge von zwei bit_sets wird mit dem Und-Operator (Operator &) gebildet. Es kann die Zuweisungsform des Operators verwendet werden. In der Zuweisungsform wird das Ziel die Disjunktion der beiden Mengen.

bset_three = bset_two & bset_four;

bset_five &= bset_three;

Die Vereinigungsmenge zweier Mengen wird auf ähnliche Weise mit dem Oder-Operator (Operator |) gebildet. Das exklusive Oder wird durch Verwendung des bitweisen exklusiven Oder-Operators (Operator ^) gebildet.

Die Operatoren Nach-Links-Verschieben und Nach-Rechts-Verschieben  (Operator << und >>) werden verwendet, um ein bit_set nach links oder rechts zu verschieben; dies geschieht analog zu der Verwendung dieser Operatoren mit Integerargumenten. Wenn ein Bit um einen Integerwert n nach links verschoben wird, dann ist die neue Bit-Position i der Wert des früheren i-n. Nullen werden in die neuen Positionen verschoben.

Umwandlungen

Die Elementfunktion to_ulong() wandelt ein bit_set in einen Typ unsigned long um. Wird diese Operation an einem bit_set ausgeführt, das mehr Elemente enthält, als in diesem Typ dargestellt werden können, löst das einen Fehler aus.

Die Elementfunktion to_string() wandelt ein bit_set in ein Objekt vom Typ string um. Der String enthält so viele Zeichen wie das bit_set. Jedes Null-Bit entspricht dem Zeichen 0, jedes 1-Bit wird durch das Zeichen 1 dargestellt.


Map und Multimap

9.1 Die Datenabstraktion Map

9.2 Map- und Multimap-Operationen

9.3 Beispielprogramme


Die Datenabstraktion Map

Hinweis:	In anderen Programmiersprachen wird eine der Map ähnliche Datenstruktur gelegentlich als dictionary, table oder als associative array bezeichnet.

Eine Map ist eine indizierte Datenstruktur, ähnlich einem Vektor oder einem Deque. Maps unterscheiden sich von Vektoren oder Deques aber in zwei entscheidenden Punkten. Erstens brauchen in einer Map - im Gegensatz zu einem Vektor oder einem Deque - die Indexwerte (Schlüsselwerte genannt) nicht Ganzzahlen zu sein, sondern können jeden geordneten Datentyp haben. Zum Beispiel können Maps mit Realzahlen oder mit Strings indiziert sein. Jeder Datentyp, für den ein Vergleichsoperator definiert werden kann, kann als Schlüssel verwendet werden. Wie bei einem Vektor oder einem Deque kann auf Elemente über einen Indizierungsoperator zugegriffen werden (obwohl es auch noch andere Verfahren gibt). Der zweite wichtige Unterschied besteht darin, daß eine Map eine geordnete Datenstruktur ist. Das bedeutet, daß Elemente in einer Reihenfolge gehalten werden, die Ordnung wird durch Schlüsselwerte festgelegt. Da Maps die Werte in einer Ordnung verwalten, können sie ein Element, das durch einen beliebigen angegebenen Schlüssel bezeichnet wird, sehr schnell finden. (Der Zeitaufwand bei Suchvorgängen steigt logarithmisch an). Wie bei Listen ist die Größe von Maps nicht beschränkt; sie werden größer oder kleiner, wenn neue Elemente eingefügt oder entfernt werden.

Die Standardbibliothek stellt zwei Arten von Maps zur Verfügung. Die Datenstruktur Map benötigt eindeutige Schlüssel. Das heißt, es besteht eine eins-zu-eins-Beziehung zwischen Schlüsselelementen und den ihnen entsprechenden Werten. In einer Map wird der Versuch ignoriert, einen neuen Wert einzufügen, der einen bereits vorhandenen Schlüssel verwendet. Ein Multimap gestattet es demgegenüber, mehrere verschiedene Einträge mit dem gleichen Schlüssel zu indizieren. Beide Datenstrukturen bieten relativ schnelle (logarithmischer Zeitbedarf) Einfüge-, Lösch- und Zugriffsoperationen.

Hinweis:	Wenn Sie den Datentyp pair ohne die Datenstruktur Map verwenden möchten, sollten Sie die Header-Datei mit dem Namen utility in Ihr Programm aufnehmen.

Zu einem großen Teil läßt sich eine Map einfach als ein Set verstehen, das eine Kollektion von Paaren (pairs) enthält. Die Datenstruktur pair ist eine Zweiergruppe von Werten. Auf den ersten Wert wird über den Feldnamen first zugegriffen, während der zweite naturgemäß second heißt. Eine Funktion mit dem Namen make_pair() vereinfacht das Erzeugen einer Instanz der Klasse pair.

template <class T1, class T2>

struct pair {

T1 first;

T2 second;

pair (const T1 & x, const T2 & y) : first(x), second(y) { }

};

template <class T1, class T2>

inline pair<T1, T2> make_pair(const T1& x, const T2& y)

{ return pair<T1, T2>(x, y); }

Um die Äquivalenz von Schlüsseln (Indexwerten) festzustellen - z.B. um herauszufinden, ob der Schlüsselteil eines neuen Elements mit irgendeinem bereits vorhandenen Schlüssel übereinstimmt - wird die Vergleichsfunktion für Schlüssel verwendet, nicht der Äquivalenzoperator (==). Zwei Schlüssel werden als äquivalent angesehen, wenn die Vergleichsfunktion, die zum Ordnen der Schlüsselwerte verwendet wird, in beiden Richtungen false ergibt. Das heißt also, wenn Compare(key1, key2) false ist und wenn Compare(key2, key1) ebenfalls false ist, dann werden key1 und key2 als äquivalent angesehen.


Map-Include-Dateien

Wenn Sie eine Map oder eine Multimap verwenden möchten, müssen Sie die Header-Datei map oder multimap in Ihr Programm aufnehmen.

# include <map>


Map- und Multimap-Operationen

Die folgende Tabelle faßt die Elementfunktionen zusammen, die von den Datentypen Map und Multimap zur Verfügung gestellt werden. Jede von ihnen wird weiter unten ausführlicher besprochen. Beachten Sie, daß die Elementfunktionen zwar die Grundoperationen zur Verfügung stellen, die Nützlichkeit der Datenstruktur aber durch die Verwendung der generischen Algorithmen, die unter Überblick über generische Algorithmen und Überblick über Algorithmen für geordnete Kollektionen beschrieben sind, erheblich erweitert wird.

Ergebnis	Name	Argumente

	map	()
	multimap	()
	map	(Compare)
	multimap	(Compare)
	map	template <class Iterator> (Iterator, Iterator)
	multimap	template <class Iterator> (Iterator, Iterator)
	map	(const map)
	multimap	(const multimap)
BidirectionalIterator	begin	()
size_type	count	(key_type)
bool	empty	()
BidirectionalIterator	end	()
pair<iterator, iterator>	equal_range	(key_type)
void	erase	(iterator)
size_type	erase	(key_type)
void	erase	(iterator, iterator)
iterator	find	(key_type)
Function	key_comp	()
pair<iterator, bool>	insert	(value_type)
iterator	insert	(iterator, value_type)
void	insert	template <class Iterator> (Iterator, Iterator)
iterator	lower_bound	(key_type)
size_type	max_size	()
BidirectionalIterator	rbegin	()
BidirectionalIterator	rend	()
size_type	size	()
void	swap	(map)
iterator	upper_bound	(key_type)
Function	value_comp	()
reference	operator []	key_type (map only)
map	operator =	(map)


Erzeugung und Initialisierung

Die Deklaration einer Map folgt dem Muster, dem wir schon wiederholt in der Standardbibliothek begegnet sind. Eine Map ist eine Template-Datenstruktur, die durch den Typ der Schlüsselelemente, den Typ der mit ihnen verbundenen Werte und den Operator, der für den Vergleich der Schlüssel verwendet werden soll, spezialisiert wird. Wenn Ihr Compiler Standard-Template-Typen unterstützt (ein relativ neues Merkmal in C++, das noch nicht von allen Anbietern unterstützt wird), dann ist der letzte Punkt optional, und wenn die Angabe fortgelassen wird, wird vom Kleiner-als-Operator für den Schlüsselvergleich ausgegangen. Maps können ohne Initialwerte deklariert werden oder mit Werten aus einem anderen Container initialisiert werden, wenn ein Iteratorpaar zur Verfügung gestellt wird. Im zweiten Fall müssen die Iteratoren Werte vom Typ pair bezeichnen. Das erste Feld in jedem solchen Paar wird als Schlüssel angesehen, während das zweite Feld der Wert ist. Ein Kopierkonstruktor erlaubt auch das Erzeugen von Maps als Kopien anderer Maps.

// Map, die mit doubles indiziert ist und strings enthält

map<double, string, less<double> > map_one;

// Map, die mit Integern indiziert ist und Integer enthält

map<int, int> map_two(aContainer.begin(), aContainer.end());

// Erzeugen einer neuen Map, die aus map two initialisiert wird

map<int, int> map_three (map_two);	// Kopierkonstruktor

Eine Map kann einer anderen Map zugewiesen werden, und zwei Maps können ihre Werte mittels der Operation swap() tauschen (in analoger Weise zu anderen Containern der Standardbibliothek).


Typdefinitionen

Die Klassen Map und Multimap enthalten eine Reihe von Typdefinitionen. Diese werden meistens in Deklarationsanweisungen verwendet. Beispielsweise kann ein Iterator für eine Map von Strings auf Integer in folgender Weise deklariert werden:

map<string, int>::iterator location;

Außer iterator sind in Map und Multimap die folgenden Typen definiert:

key_type	Der Typ der Schlüssel, die für die Indizierung der Map verwendet werden.
value_type	Der Typ, der vom Container gehalten wird, ein Schlüssel/Wert-Paar.
const_iterator	Ein Iterator, der keine Veränderung der zugrundeliegenden Sequenz erlaubt.
reverse_iterator	Ein Iterator, der in umgekehrter Richtung fortschreitet.
const_reverse_iterator	Eine Kombination aus konstantem und Reverse-Iterator.
reference	Eine Referenz auf einen zugrundeliegenden Wert.
const_reference	Eine Referenz auf einen zugrundeliegenden Wert, die keine Veränderung des Elements erlaubt.
size_type	Ein vorzeichenloser Integertyp, der für die Größenangabe von Containern verwendet wird.
key_compare	Ein Funktionsobjekt, das für den Vergleich zweier Schlüssel verwendet werden kann.
value_compare	Ein Funktionsobjekt, das für den Vergleich zweier Elemente verwendet werden kann.
difference_type	Ein Integertyp mit Vorzeichen, der die Entfernung zwischen Iteratoren angibt.


Einfügung und Zugriff

Werte können in eine Map oder eine Multimap mit der Operation insert() eingefügt werden. Beachten Sie, daß das Argument ein Schlüssel-Wert-Paar sein muß. Dieses Paar wird oft unter Verwendung des Datentyps value_type konstruiert, der mit der Map verbunden ist.

map_three.insert (map<int>::value_type(5, 7));

Einfügungen können auch mit Hilfe eines Iteratorpaars vorgenommen werden, beispielsweise, wenn die Werte aus einer anderen Map stammen.

map_two.insert (map_three.begin(), map_three.end());

Bei einer Map (nicht aber bei einer Multimap) kann der Indizierungsoperator für den Zugriff auf Werte und ihre Einfügung verwendet werden. Wird einfach ein Schlüssel als Indexangabe (subscript) verwendet, wird ein Eintrag erzeugt: Das Vorgabeelement wird als damit verbundener Wert verwendet. Zuweisungen zu dem Ergebnis der Indizierung ändern die zugeordnete Bindung.

cout << "Index value 7 is " << map_three[7] << endl;

// jetzt den verbundenen Wert ändern

map_three[7] = 5;

cout << "Index value 7 is " << map_three[7] << endl;


Entfernen von Werten

Werte können aus einer Map oder einer Multimap durch Angeben des Schlüsselwerts entfernt werden. In einer Multimap werden dann alle Elemente mit dem betreffenden Schlüssel entfernt. Ein zu entfernendes Element kann auch durch einen Iterator bezeichnet werden; beispielsweise durch den Operator, der eine find()-Operation zurückliefert. Ein Iteratorpaar kann verwendet werden, um einen Bereich von Elementen im ganzen zu löschen.

// Element 4 löschen

map_three.erase(4);

// Element fünf löschen

mtesttype::iterator five = map_three.find(5);

map_three.erase(five);

// alle Werte zwischen sieben und elf löschen

mtesttype::iterator seven = map_three.find(7);

mtesttype::iterator eleven = map_three.find(11);

map_three.erase (seven, eleven);

Wenn der zugrundeliegende Elementtyp einen Destruktor zur Verfügung stellt, wird der Destruktor vor dem Entfernen des Schlüssel-Wert-Paares aus der Kollektion aufgerufen.


Iteratoren

Hinweis:	Anders als bei einem Vektor oder einem Deque werden Iteratoren, die sich auf einen anderen Teil des Containers beziehen, durch das Einfügen oder Entfernen von Elementen nicht ungültig.

Die Elementfunktionen begin() und end() erzeugen sowohl für Maps als auch für Multimaps bidirektionale Iteratoren. Das Dereferenzieren eines Iterators für eine Map oder eine Multimap ergibt ein Paar von Schlüssel/Wert-Elementen. Die Feldnamen first und second können auf diese Werte angewandt werden, um auf die einzelnen Felder zuzugreifen. Das erste Feld ist konstant und kann nicht verändert werden. Das zweite Feld kann jedoch verwendet werden, um den Wert zu ändern, der einem bestimmten Schlüssel im Container zugeordnet ist. Elemente werden nacheinander auf der Grundlage der Ordnung der Schlüsselfelder erzeugt.

Die Elementfunktionen rbegin() und rend() erzeugen Iteratoren, die die Elemente in umgekehrter Reihenfolge zurückliefern.


Suchen und Zählen

Die Elementfunktion size() liefert die Anzahl der Elemente, die in einem Container enthalten sind. Die Elementfunktion empty() gibt einen booleschen Wert true zurück, wenn der Container leer ist; sie ist im allgemeinen schneller als das Vergleichen der Größe mit null.

Die Elementfunktion find() nimmt einen Schlüssel als Argument an und liefert einen Iterator zurück, der das mit dem Schlüssel verbundene Schlüssel/Wert-Paar bezeichnet. Bei Multimaps wird der erste derartige Wert zurückgeliefert. In beiden Fällen wird der Bereichsendeiterator zurückgegeben, wenn kein entsprechender Wert gefunden wird.

if (map_one.find(4) != map_end.end())

cout << "contains a four" << endl;

Die Elementfunktion lower_bound() liefert den ersten Eintrag, der dem Argumentschlüssel entspricht, während die Elementfunktion upper_bound() den ersten Wert nach dem letzten Eintrag, der dem Argument entspricht, zurückgibt. Die Elementfunktion equal_range() gibt ein Iteratorenpaar zurück, das die untere und obere Bereichsgrenze angibt. Ein Beispiel, das die Verwendung dieser Prozeduren demonstriert, folgt später in diesem Abschnitt.

Die Elementfunktion count() liefert die Anzahl der Elemente zurück, die dem als Argument übergebenen Schlüsselwert entsprechen. Bei einer Map ist dieser Wert immer entweder null oder eins, bei einer Multimap kann es jeder nicht negative Wert sein. Wenn einfach nur festgestellt werden soll, ob eine Kollektion ein Element enthält, das durch einen vorhandenen Schlüssel indiziert ist, ist die Verwendung von count() häufig einfacher als die Verwendung der Funktion find() mit anschließendem Vergleich des Rückgabewerts mit dem Bereichsendeiterator.

if (map_one.count(4))

cout << "contains a four" << endl;


Elementvergleiche

Die Elementfunktionen key_comp() und value_comp(), die keine Argumente annehmen, liefern eine Funktion zurück, die zum Vergleich von Elementen der Typen Schlüssel bzw. Wert verwendet werden kann. Werte, die in diesen Vergleichen verwendet werden, brauchen nicht in der Kollektion enthalten zu sein, und keine der beiden Funktionen wirkt sich in irgendeiner Hinsicht auf den Container aus.

if (map_two.key_comp()(i, j))

cout << "element i is less than j" << endl;


Andere Map-Operationen

Weil Maps und Multimaps geordnete Kollektionen darstellen und weil die Iteratoren für Maps Paare (pairs) zurückliefern, sind viele der unter Überblick über generische Algorithmen und Überblick über Algorithmen für geordnete Kollektionen beschriebenen Funktionen bedeutungslos oder schwierig zu verwenden. Es gibt jedoch einige Ausnahmen. Die Funktionen for_each() (Anwenden einer Funktion auf alle Elemente in einer Kollektion), adjacent_find() (Suchen aufeinanderfolgender doppelter Elemente) und accumulate() (Reduzieren einer Sequenz auf einen Einzelwert) können in normaler Weise verwendet werden. In allen Fällen ist jedoch darauf zu achten, daß die als Argumente verwendeten Funktionen ihrerseits ein Schlüssel/Wert-Paar als Argument annehmen.


Beispielprogramme

Drei Beispielprogramme sollen die Verwendung von Maps und Multimaps verdeutlichen. Es handelt sich um eine Telefondatenbank, um Diagramme und um eine Konkordanz.


Beispielprogramm: eine Telefondatenbank
Beispielprogramme: eine Telefondatenbank

Hinweis:	Das vollständige Beispielprogramm befindet sich in der Datei tele.cpp auf dem mitgelieferten Datenträger.

Ein Wartungsprogramm für eine einfache Telefondatenbank stellt eine gute Einsatzmöglichkeit für eine Map dar. Die Datenbank ist einfach nur eine indizierte Struktur, mit dem Namen der Person oder der Firma (einem String) als Schlüsselwert und der Telefonnummer (einem long integer) als dem damit verbundenen Eintrag. Eine solche Klasse könnte wie folgt implementiert werden:

typedef map<string, long, less<string> > friendMap;

typedef friendMap::value_type entry_type;

class telephoneDirectory {

public:

void addEntry (string name, long number)	// neuen Eintrag in Datenbank aufnehmen

{ database[name] = number; }

void remove (string name)	// Eintrag aus der Datenbank entfernen

{ database.erase(name); }

void update (string name, long number)	// Eintrag aktualisieren

{ remove(name); addEntry(name, number); }

void displayDatabase()	// gesamte Datenbank anzeigen

{ for_each(database.begin(), database.end(), printEntry); }

void displayPrefix(int);	// Einträge mit übereinstimmender Vorwahlnummer anzeigen

void displayByPrefix();	// Datenbank nach Vorwahlnummern sortiert anzeigen

private:

friendMap database;

};

Einfache Operationen auf der Datenbank sind direkt durch Map-Befehle implementiert. Das Hinzufügen eines Elements zu der Datenbank besteht schlicht in einem insert, das Entfernen eines Elements in einem erase, Aktualisieren in einer Kombination dieser beiden. Sollen alle Einträge der Datenbank gedruckt werden, kann der Algorithmus for_each() zum Einsatz kommen, der die folgende einfache Routine auf jeden Eintrag anwendet:

void printEntry(const entry_type & entry)

{ cout << entry.first << ":" << entry.second << endl; }

Einige etwas komplexere Operationen sollen zeigen, wie einige der unter Überblick über generische Algorithmen beschriebenen Algorithmen zusammen mit Maps verwendet werden können. Angenommen, es sollen alle Rufnummern, die mit einer bestimmten dreistelligen Vorwahlbeginnen, angezeigt werden. Dazu wird die Funktion find_if() verwendet (die sich von der Elementfunktion find() der Klasse Map unterscheidet), um den ersten Eintrag zu finden. Von dieser Position ausgehend, werden wiederholte Aufrufe von find_if() jeden nachfolgenden passenden Eintrag finden.

void telephoneDirectory::displayPrefix(int prefix)

{

cout << "Listing for prefix " << prefix << endl;

friendMap::iterator where;

where =

find_if (database.begin(), database.end(),

 checkPrefix(prefix));

while (where != database.end()) {

printEntry(*where);

where = find_if (++where, database.end(),

 checkPrefix(prefix));

}

cout << "end of prefix listing" << endl;

}

Als Prädikat für diese Operation ist eine boolesche Funktion erforderlich, die nur ein einzelnes Argument annimmt (das Paar, das einen Datenbankeintrag repräsentiert), und angibt, ob es sich um die gesuchte Vorwahlnummer handelt. Es gibt keine erkennbare "Kandidaten"-Funktion, und die Testvorwahl wird der Vergleichsfunktion nicht als Argument übergeben. Die Lösung dieses Problems besteht im Einsatz eines Verfahrens, das in der Standardbibliothek häufig verwendet wird, nämlich die Prädikatfunktion als Instanz einer Klasse zu definieren, und das Testprädikat als Instanzvariable in der Klasse zu speichern, die beim Konstruieren der Klasse initialisiert wird. Die gewünschte Funktion wird dann als Funktionsaufruf-Operator für die Klasse definiert:

int prefix(const pair<string, long> entry)

{ return entry.second / 10000; }

class checkPrefix {

public:

checkPrefix (int p) : testPrefix(p) { }

const int testPrefix;

bool operator () const (const entry_type & entry)

{ return prefix(entry) == testPrefix; }

};

Das letzte Beispiel besteht in der Anzeige des Telefonverzeichnisses nach Vorwahlnummern sortiert. Es besteht keine Möglichkeit, die interne Ordnung von Maps zu ändern. Statt dessen wird also eine neue Map mit umgekehrten Elementtypen erzeugt; wenn dann die Werte in die neue Map kopiert werden, sind die Werte automatisch nach Vorwahlnummern geordnet. Nachdem diese neue Map erzeugt wurde, kann sie angezeigt werden.

typedef map<long, string, less<long> > sortedMap;

typedef sortedMap::value_type sorted_entry_type;

void telephoneDirectory::displayByPrefix()

{

cout << "Display by prefix" << endl;

sortedMap sortedData;

friendMap::iterator itr;

for (itr = database.begin(); itr != database.end(); itr++)

sortedData.insert(sortedMap::value_type((*itr).second,

(*itr).first));

for_each(sortedData.begin(), sortedData.end(), printSortedEntry);

}

Die Funktion zum Anzeigen der sortierten Einträge sieht so aus:

void printSortedEntry (const sorted_entry_type & entry)

{ cout << entry.first << ":" << entry.second << endl; }


Beispielprogramm: Graphs

Hinweis:	Die ausführbare Version dieses Programms befindet sich in der Datei graph.cpp auf dem mitgelieferten Datenträger.

Eine Map, deren Elemente ihrerseits Maps sind, ist eine naheliegende Repräsentation eines orientierten Graphs (directed graph). Angenommen, die Namen von Städten werden durch Strings bezeichnet, und es soll nun eine Map konstruiert werden, in der der zu einer Kante gehörende Wert die Entfernung zwischen zwei miteinander verbundenen Städten ist. Ein solcher Graph könnte folgendermaßen konstruiert werden:

typedef map<string, int> stringVector;

typedef map<string, stringVector> graph;

string pendleton("Pendleton");	// Strings für Städtenamen definieren

string pensacola("Pensacola");

string peoria("Peoria");

string phoenix("Phoenix");

string pierre("Pierre");

string pittsburgh("Pittsburgh");

string princeton("Princeton");

string pueblo("Pueblo");

graph cityMap;	// den Graph deklarieren, der die Map enthält

cityMap[pendleton][phoenix] = 4;	// dem Graph Kanten hinzufügen

cityMap[pendleton][pueblo] = 8;

cityMap[pensacola][phoenix] = 5;

cityMap[peoria][pittsburgh] = 5;

cityMap[peoria][pueblo] = 3;

cityMap[phoenix][peoria] = 4;

cityMap[phoenix][pittsburgh] = 10;

cityMap[phoenix][pueblo] = 3;

cityMap[pierre][pendleton] = 2;

cityMap[pittsburgh][pensacola] = 4;

cityMap[princeton][pittsburgh] = 2;

cityMap[pueblo][pierre] = 3;

Der Typ stringVector ist eine Map aus Integern, die durch Strings indiziert sind. Der Typ graph ist im Prinzip ein zweidimensionales Sparse Array, das mit Strings indiziert ist und Integerwerte enthält. Graph wird mit einer Folge von Zuweisungen initialisiert.

Graphs, die auf diese Weise repräsentiert werden, können mit einer Reihe klassischer Algorithmen bearbeitet werden. Ein Beispiel ist Dijkstras Algorithmus der kürzesten Verbindung. Dijkstras Algorithmus beginnt an einer bestimmten Stadt, die als Ausgangspunkt angegeben wird. Dann wird eine priority_queue aus Entfernungs/Stadt-Paaren (pairs) konstruiert und mit der Entfernung der Ausgangsstadt zu sich selbst (nämlich null) initialisiert. Die Definition für den Datentyp Distance Pair ist die folgende:

struct DistancePair {

unsigned int first;

string second;

DistancePair() : first(0) { }

DistancePair(unsigned int f, const string & s)

: first(f), second(s) { }

};

bool operator < (const DistancePair & lhs, const DistancePair & rhs)

{ return lhs.first < rhs.first; }

Beachten Sie, daß im folgenden Algorithmus die Bedingungsprüfung für die Ereigniswarteschlange umgekehrt ist, da bei jedem Schritt der kleinste, nicht der größte Wert aus der Kollektion gezogen werden soll. Bei jedem Durchgang durch die Schleife wird eine Stadt aus der Queue gezogen. Sofern noch kein kürzerer Weg zu dieser Stadt gefunden wurde, wird die aktuelle Entfernung gespeichert und der Graph untersucht, um die Entfernung dieser Stadt zu jeder ihrer Nachbarstädte zu berechnen. Dieser Prozeß wird fortgesetzt, bis die Ereigniswarteschlange ausgeschöpft ist.

void shortestDistance(graph & cityMap,

const string & start, stringVector & distances)

{

// Bearbeiten einer Ereigniswarteschlange von Entfernungen zu Städten

priority_queue<DistancePair, vector<DistancePair>,

greater<DinstancePair> > que;

que.push(DistancePair(0, start));

while (! que.empty()) {

// nächstgelegene Stadt aus der Queue ziehen

int distance = que.top().first;

string city = que.top().second;

que.pop();

// bearbeiten, wenn noch nicht bekannt

if (0 == distances.count(city)) {

// dann der Map mit den kürzesten Entfernungen hinzufügen

distances[city] = distance;

// und Werte in die Queue einsetzen

const stringVector & cities = cityMap[city];

stringVector::const_iterator start = cities.begin();

stringVector::const_iterator stop = cities.end();

for (; start != stop; ++start)

que.push(DinstancePair(distance + (*start).second, 	(*start).first));

}

}

}

Beachten Sie, daß dieser relativ einfache Algorithmus mit Vektoren, Maps, Strings und Ereigniswarteschlangen arbeitet.


Beispielprogramm: eine Konkordanz

Hinweis:	Eine ausführbare Version des Konkordanzprogramms befindet sich in der Datei concord.cpp auf dem mitgelieferten Datenträger.

Eine Konkordanz ist ein alphabetisches Verzeichnis von Wörtern in einem Text, das die Nummern der Zeilen angibt, in denen jedes Wort auftaucht. Die hier vorgestellte Konkordanz soll die Verwendung der Container-Klassen Map und Multimap veranschaulichen. Die Datenwerte in der Konkordanz werden von einer Multimap gehalten. Sie sind durch Strings indiziert (nämlich durch die Wörter) und enthalten Integer (die Zeilennummern). Eine Multimap kommt hier deshalb zum Einsatz, weil das gleiche Wort häufig in verschiedenen Zeilen auftreten wird. Tatsächlich ist ja das Offenlegen dieser Zusammenhänge einer der Hauptzwecke einer Konkordanz. Eine alternative Implementierung könnte eine Map und einen Satz von Integerelementen als zugehörige Werte verwenden.

class concordance {

typedef multimap<string, int> wordDictType;

public:

void addWord (string, int);

void readText (istream &);

void printConcordance (ostream &);

private:

wordDictType wordMap;

};

Die Erzeugung der Konkordanz ist in zwei Schritte gegliedert: zuerst erzeugt das Programm die Konkordanz (indem es Zeilen aus einem Eingabe-Stream liest), und anschließend schreibt es das Ergebnis in einen Ausgabe-Stream. Dies spiegelt sich in den zwei Elementfunktionen readText() und printConcordance() wieder. Die erste, readText(), ist wie folgt implementiert:

void concordance::readText (istream & in)

{

string line;

for (int i = 1; getline(in, line, '\n'); i++) {

allLower(line);

list<string> words;

split (line, " ,.;:", words);

list<string>::iterator wptr;

for (wptr = words.begin(); wptr != words.end(); ++wptr)

addWord(*wptr, i);

}

}

Zeilen werden nacheinander jeweils einzeln aus dem Eingabe-Stream gelesen. Der Text der Zeile wird zunächst in Kleinschreibung umgewandelt, dann wird die Zeile in Wörter aufgeteilt. Dazu wird die Funktion split() verwendet, die unter Beispielfunktion: Aufteilen einer Zeile in Wörter beschrieben ist. Dann wird jedes Wort in die Konkordanz aufgenommen. Die Methode, die zum Eintragen eines Werts in die Konkordanz verwendet wird, sieht so aus:

void concordance::addWord (string word, int line)

{

// prüfen, ob das Wort in der Liste vorkommt

// zuerst Bereich der Einträge mit gleichem Schlüssel anfordern

wordDictType::iterator low = wordMap.lower_bound(word);

wordDictType::iterator high = wordMap.upper_bound(word);

// Einträge in Schleife durchgehen, auf Übereinstimmung mit der aktuellen Zeile prüfen

for ( ; low != high; ++low)

if ((*low).second == line)

return;

// nicht aufgetreten, jetzt aufnehmen

wordMap.insert(wordDictType::value_type(word, line));

}

Der größte Teil von addWord() hat die Aufgabe sicherzustellen, daß Werte in der Map von Wörtern nicht dupliziert werden, wenn das gleiche Wort zweimal in einer Zeile auftritt. Um dies zu erreichen, wird zunächst der mit dem Schlüssel übereinstimmende Wertebereich geprüft, und wenn sich die aktuelle Zeilennummer darin findet, wird keine Einfügung vorgenommen. Nur wenn die Schleife vollständig abgearbeitet wurde ohne die Zeilennummer zu finden, wird das neue Paar Wort/Zeilennummer eingefügt.

Der letzte Schritt besteht im Drucken der Konkordanz. Dies geschieht folgendermaßen:

void concordance::printConcordance (ostream & out)

{

string lastword("");

wordDictType::iterator pairPtr;

wordDictType::iterator stop = wordMap.end();

for (pairPtr = wordMap.begin(); pairPtr != stop; ++pairPtr)

// wenn das Wort mit dem vorangegangenen übereinstimmt, nur Zeilennummer drucken

if (lastword == (*pairPtr).first)

out << " " << (*pairPtr).second;

else {	// erster Eintrag des Worts

lastword = (*pairPtr).first;

cout << endl << lastword << ": " << (*pairPtr).second;

}

cout << endl; // letzte Zeile beenden

}

Die in der Wortliste gehaltenen Elemente werden mit einer Iteratorschleife abgearbeitet. Jedes neue Wort erzeugt eine neue Ausgabezeile, dahinter stehen die Zeilennummern durch Leerzeichen getrennt. Wenn die Eingabe beispielsweise folgender Text war:

It was the best of times,

it was the worst of times.

Dann wäre die Ausgabe in alphabetischer Reihenfolge:

best: 1

it: 1 2

of: 1 2

the: 1 2

times: 1 2

was: 1 2

worst: 1


Stack und Queue

10.1 Überblick über Stack und Queue

10.2 Die Datenabstraktion Stack
10.3 Die Datenabstraktion Queue


Überblick über Stack und Queue

Die meisten Menschen haben ein gutes intuitives Verständnis der Datenabstraktionen Stack und Queue, das sich auf Alltagserfahrung stützt. Ein exzellentes Beispiel für einen Stack (Stapel) ist ein Stoß Papier auf einem Schreibtisch oder ein Stapel Teller im Küchenschrank. In beiden Fällen ist das wichtigste Merkmal, daß das zuoberst befindliche Element am leichtesten zu erreichen ist, also den besten Zugriff bietet. Die einfachste Art, der Kollektion ein neues Element hinzuzufügen ist, es einfach über den aktuell im Stapel befindlichen Elementen abzulegen. Auf diese Weise ist ein von einem Stapel entferntes Element dasjenige, das diesem zuletzt hinzugefügt wurde; also zum Beispiel das oberste Blatt Papier oder der oberste Teller.

Hinweis:	Ein Stack wird manchmal als eine LIFO-Struktur bezeichnet, eine Queue als FIFO-Struktur. Die Abkürzung LIFO bedeutet Last In, First Out. Gemeint ist: das erste aus einem Stack entfernte Element ist dasjenige, das ihm zuletzt hinzugefügt wurde. Der Ausdruck FIFO ist demgegenüber die Kurzform von First In, First Out. Das bedeutet, das erste aus einer Queue entfernte Element ist dasjenige, das auch zuerst in sie eingefügt wurde.

Ein Alltagsbeispiel für eine Queue (Warteschlange) ist die Warteschlange vor einem Bankschalter oder eine Reihe von Menschen, die vor einem Theater anstehen. Hier erfolgen "Neuzugänge" am Ende der Schlange, wenn nämlich Leute hinzukommen, die auch in das Theater möchten, während das Entfernen von Elementen vom Kopf der Schlange erfolgt, wenn nämlich Menschen das Theater betreten. Die Reihenfolge für Entfernungsvorgänge ist also bei einer Queue gerade umgekehrt wie bei einem Stack. In einer Queue ist das entfernte Element dasjenige, das die längste Anwesenheitsdauer alle Elemente in der Queue aufweist.

In der Standardbibliothek sind sowohl Stack als auch Queue Adapter, die auf andere Container aufbauen. Diese anderen Container enthalten die Werte. Ein Stack kann entweder aus einem Vektor oder aus einem Deque erzeugt werden, während eine Queue auf einer Liste oder auf einem Deque aufbauen kann. Elemente, die in einem Stack oder in einer Queue enthalten sind, müssen sowohl den Operator < als auch den Operator == kennen.

Weil weder Stacks noch Queues Iteratoren definieren, können die Elemente einer Kollektion nicht untersucht werden, es sei denn, sie werden einzeln aus der Kollektion entfernt. Die Tatsache, daß diese Strukturen keine Iteratoren implementieren bringt zugleich mit sich, daß die meisten der generischen Algorithmen, die unter Überblick über generische Algorithmen und Überblick über Algorithmen für geordnete Kollektionen beschrieben sind, mit diesen beiden Datenstrukturen nicht verwendet werden können.


Die Datenabstraktion Stack

Als Datenabstraktion wird ein Stack traditionell als beliebiges Objekt definiert, das folgende Operationen implementiert:

empty()	liefert true zurück, wenn die Kollektion leer ist

size()	liefert die Anzahl der Elemente in der Kollektion zurück

top()	liefert das oberste Element auf dem Stack zurück (entfernt es jedoch nicht)

push(newElement)	legt ein neues Element auf dem Stack ab

pop()	entfernt das oberste Element vom Stack (ohne es zurückzuliefern)

Beachten Sie, daß der Zugriff auf das oberste Element und das Entfernen des obersten Elements getrennte Operationen sind. Programme, die die Datenabstraktion Stack verwenden, sollten die Datei stack einbinden und außerdem die Include-Datei für den Containertyp (z.B. vector).

# include <stack>

# include <vector>

Hinweis:	Beachten Sie, daß bei den meisten Compilern ein Leerschritt zwischen den beiden spitzen Klammern in der Deklaration eines Stacks erforderlich ist, weil diese sonst vom Compiler als Operator Nach Rechts Verschieben interpretiert werden.

Eine Deklaration für einen Stack muß zwei Argumente angeben, den zugrundeliegenden Elementtyp und den Container, der die Elemente enthält. Für einen Stack sind ein Vektor oder ein Deque üblich, es kann aber auch eine Liste verwendet werden. Die Vektorversion ist im allgemeinen kleiner, während eine Implementierung mittels Deque unter Umständen etwas schneller ausgeführt wird. Dies sind einige Beispieldeklarationen für einen Stack:

stack< int, vector<int> > stackOne;

stack< double, deque<double> > stackTwo;

stack< Part *, list<Part * > > stackThree;

stack< Customer, list<Customer> > stackFour;

Das letzte Beispiel erzeugt einen Stack eines vom Programmierer definierten Typs mit dem Namen Customer.


Beispielprogramm: ein RPN-Taschenrechner

Eine klassische Anwendung für einen Stack ist ein Taschenrechner. Die Eingabe in den Rechner besteht aus einem Text-String, der einen Ausdruck in umgekehrter polnischer Notation (RPN) darstellt. Operanden, also Integerkonstanten, werden auf einem Stack mit Werten abgelegt. Wenn Operatoren angetroffen werden, wird die passende Menge Operanden vom Stack gezogen, die Operation wird ausgeführt, und das Ergebnis wird zum Schluß wieder auf dem Stack abgelegt.

Hinweis:	Dieses Programm befindet sich in der Datei calc.cpp auf dem mitgelieferten Datenträger.

In der Entwicklung der Stack-Simulation lassen sich zwei Teile unterscheiden. Eine Rechner-Engine übernimmt die eigentliche Arbeit in der Simulation, sie führt aber keine Ein- oder Ausgabeoperationen aus. Der Name weist auf die Analogie zu einem Automotor oder einem Computerprozessor hin - der Mechanismus führt die eigentliche Arbeit aus, aber der Anwender dieses Mechanismus steht mit ihm normalerweise nicht in direktem Kontakt. Diese Maschine ist von dem Taschenrechnerprogramm umgeben, das mit dem Anwender in Kontakt steht und die passenden Anweisungen an die Rechner-Engine übergibt.

Die folgende Klassendefinition kann für die Rechner-Engine verwendet werden. Innerhalb der Klassendeklaration wird eine Aufzählungsliste von Werten definiert, die jeden der vom Rechner akzeptierten Operatoren repräsentieren. Wir gehen zur Vereinfachung von zwei Annahmen aus: alle Operanden sind Integerwerte, und es werden nur binäre Operatoren behandelt.

class calculatorEngine {

public:

enum binaryOperator {plus, minus, times, divide};

int currentMemory ()	// liefert das aktuelle oberste Element vom Stack zurück

{ return data.top(); }

void pushOperand (int value)	// Operandenwert auf dem Stack ablegen

{ data.push (value); }

void doOperator (binaryOperator);	// Oberstes Element vom Stack nehmen und Operator ausführen

protected:

stack< int, vector<int> > data;

};

Hinweis:	Ein stabileres Programm würde erst prüfen, ob der Stack nicht leer ist, bevor es die pop()-Operation durchzuführen versucht.

Die Elementfunktion doOperator() führt die eigentliche Arbeit aus. Sie holt Werte vom Stack, führt die Operation aus und legt das Ergebnis anschließend wieder auf dem Stack ab.

void calculatorEngine::doOperator (binaryOperator theOp)

{

int right = data.top();	// oberstes Element lesen

data.pop();	// Element vom Stack abheben

int left = data.top();	// nächstes oberstes Element lesen

data.pop();	// Element vom Stack abheben

switch (theOp) {

case plus: data.push(left + right); break;

case minus: data.push(left - right); break;

case times: data.push(left * right); break;

case divide: data.push(left / right); break;

}

}

Das Hauptprogramm liest Werte in umgekehrter polnischer Notation und ruft die Rechner-Engine auf, die die eigentliche Arbeit ausführt:

void main() {

int intval;

calculatorEngine calc;

char c;

while (cin >> c)

switch (c) {

case '0': case '1': case '2': case '3': case '4':

case '5': case '6': case '7': case '8': case '9':

cin.putback(c);

cin >> intval;

calc.pushOperand(intval);

break;

case '+':  calc.doOperator(calculatorEngine::plus);

break;

case '-':  calc.doOperator(calculatorEngine::minus);

break;

case '*':  calc.doOperator(calculatorEngine::times);

break;

case '/':  calc.doOperator(calculatorEngine::divide);

break;

case 'p':  cout << calc.currentMemory() << endl;

break;

case 'q':  return; // Programm verlassen

}

}


Die Datenabstraktion Queue

Als Datenabstraktion ist eine Queue traditionell als beliebiges Objekt definiert, das folgende Operationen implementiert:

empty()	liefert true zurück, wenn die Kollektion leer ist

size()	liefert die Anzahl der Elemente in der Kollektion zurück

front()	liefert das Element am Anfang der Queue zurück (entfernt es jedoch nicht)

back()	liefert das Element am Ende der Queue zurück

push(newElement)	fügt ein neues Element am Ende der Queue ein

pop()	entfernt das Element am Anfang der Queue (ohne es zurückzuliefern)

Beachten Sie, daß der Zugriff auf das vorderste Element und das Entfernen des vordersten Elements unterschiedliche Operationen sind. Programme, die die Datenabstraktion Queue verwenden, sollten die Datei queue einbinden sowie die Include-Datei für den Container-Typ (z.B. list).

# include <queue>

# include <list>

Eine Deklaration für eine Queue muß sowohl den Elementtyp als auch den Container, der die Werte hält, angeben. Die gebräuchlichsten Container-Typen für Queues sind List und Deque. Die List-Version ist im allgemeinen kleiner, während die Deque-Version etwas schneller sein kann. Es folgen einige Beispieldeklarationen für eine Queue.

queue< int, list<int> > queueOne;

queue< double, deque<double> > queueTwo;

queue< Part *, list<Part * > > queueThree;

queue< Customer, list<Customer> > queueFour;

Das letzte Beispiel erzeugt eine Queue von einem durch den Programmierer definierten Typ mit dem Namen Customer. Ebenso wie beim Container Stack müssen alle Objekte, die in einer Queue gespeichert werden, die Operatoren < und == erkennen.

Da Queues keine Iteratoren implementieren, können die unter Überblick über generische Algorithmen oder Überblick über Algorithmen für geordnete Kollektionen beschriebenen Algorithmen nicht angewendet werden.


Beispielprogramm: Simulation eines Bankschalters

Hinweis:	Die vollständige Version des Simulationsprogramms für einen Bankschalter befindet sich in der Datei teller.cpp auf dem mitgelieferten Datenträger.

Warteschlangen (queues) finden sich oft in Geschäften wie Supermärkten oder Banken. Nehmen wir an, Sie sind der Filialleiter einer Bank, und Sie müssen feststellen, wie viele Kassierer zu bestimmten Zeiten eingesetzt werden sollen. Sie entscheiden sich also, eine Computersimulation zu erstellen und bestimmte Verhaltensbeobachtungen zugrunde zu legen. Sie haben beispielsweise herausgefunden, daß zu Spitzenzeiten eine neunzigprozentige Wahrscheinlichkeit besteht, daß jede Minute ein Kunde die Bank betritt.

Wir entwickeln eine Simulation, indem wir zunächst Objekte definieren, die Kunden und Kassierer darstellen. Bei den Kunden ist die uns interessierende Information, wieviel Zeit sie im Durchschnitt in der Warteschlange verbringen. So bestehen Kundenobjekte daher einfach aus zwei Integer-Datenfeldern: dem Zeitpunkt des Einreihens in die Warteschlange und dem Zeitraum, den sie am Schalter verbringen. Dieses ist ein zufällig ausgewählter Wert im Bereich zwischen 2 und 8. (Siehe dazu auch Random Access-Iteratoren, die Erläuterung der Funktion randomInteger().)

class Customer {

public:

Customer (int at = 0) : arrival_Time(at),

processTime(2 + randomInteger(6)) {}

int arrival_Time;

int processTime;

bool done()	// Sind die Bankgeschäfte erledigt?

{ return --processTime < 0; }

operator < (const Customer & c)	// nach Ankunftszeit ordnen

{ return arrival_Time < c.arrival_Time; }

operator == (const Customer & c)	// keine zwei Kunden sind gleich

{ return false; }

};

Da Objekte nur in Containern der Standardbibliothek gespeichert werden können, wenn sie auf Gleichheit und Ordnung verglichen werden können, ist es erforderlich, die Operatoren < und == für die Kunden zu definieren. Die Kunden können uns außerdem mitteilen, wenn sie ihre Bankgeschäfte erledigt haben.

Kassierer sind entweder mit der Bedienung von Kunden beschäftigt, oder sie sind frei. Daraus ergibt sich, daß jeder Kassiererwert zwei Datenfelder enthält: einen Kunden und ein boolesches Flag. Kassierer definieren eine Elementfunktion, um mitzuteilen, ob sie frei sind oder nicht, sowie eine Elementfunktion, die aufgerufen wird, wenn sie mit der Bedienung eines neuen Kunden beginnen.

class Teller {

public:

Teller() { free = true; }

bool isFree()	// frei, um neuen Kunden zu bedienen?

{ if (free) return true;

  if (customer.done())

  	free = true;

  return free;

 }

void addCustomer(Customer c)	// beginnen, den neuen Kunden zu bedienen

{	customer = c;

free = false;

}

private:

bool free;

Customer customer;

};

Das Hauptprogramm besteht aus einer großen Schleife, die einmal pro simulierter Minute abgearbeitet wird. Jede Minute wird der Schlange der wartenden Kunden mit einer Wahrscheinlichkeit von 0,9 ein neuer Kunde hinzugefügt. Jeder Kassierer wird abgefragt, und wenn er frei ist, nimmt er den nächsten Kunden aus der Schlange. Die Anzahl der bedienten Kunden und die Gesamtzeit, die sie in der Warteschlange verbracht haben, werden in Zählern festgehalten. Aus diesen beiden Werten kann im Anschluß an die Simulation ermittelt werden, wieviel Zeit ein Kunde im Durchschnitt in der Warteschlange verbringt.

void main() {

int numberOfTellers = 5;

int numberOfMinutes = 60;

double totalWait = 0;

int numberOfCustomers = 0;

vector<Teller> teller(numberOfTellers);

queue< Customer, deque<Customer> > line;

for (int time = 0; time < numberOfMinutes; time++) {

if (randomInteger(10) < 9)

line.push(Customer(time));

for (int i = 0; i < numberOfTellers; i++) {

if (teller[i].isFree() & ! line.empty()) {

Customer & frontCustomer = line.front();

numberOfCustomers++;

totalWait += (time - frontCustomer.arrival_Time);

teller[i].addCustomer(frontCustomer);

line.pop();

}

}

}

cout << "average wait:" <<

 (totalWait / numberOfCustomers) << endl;

}

Durch mehrfaches Ausführen des Programms mit unterschiedlichen Werten für die Zahl der Kassierer kann der Filialleiter die kleinste Anzahl Kassierer ermitteln, die die Kunden bedienen können, ohne daß unzumutbare Wartezeiten anfallen.


Priority_Queue

11.1 Die Datenabstraktion Priority Queue

11.2 Priority Queue-Operationen

11.3 Anwendung – Ereignisgesteuerte Simulation


Die Datenabstraktion Priority Queue

Die Datenstruktur Priority Queue ist bei Problemstellungen nützlich, die es erfordern, den größten Wert in einer Kollektion von Werten schnell und wiederholt zu finden und/oder zu entfernen. Ein alltägliches Beispiel für eine Priority Queue die Liste zu erledigender Aufgaben, die viele von uns führen, um den Überblick zu behalten. Manche Arbeiten, wie etwa "Schreibtisch aufräumen", sind nicht zwingend und können verschoben werden. Andere Aufgaben, wie "Bericht bis Montag fertigstellen" oder "Blumen für Geburtstag kaufen" sind wesentlich zeitkritischer und müssen schneller angegangen werden. Auf diese Art sortieren wir die zu erledigenden Aufgaben in der Reihenfolge ihrer Wichtigkeit (oder vielleicht eher auf der Grundlage einer Kombination ihrer Wichtigkeit, ihres Langzeitnutzens und des Vergnügens, das uns die Erledigung bereitet) und wählen das dringlichste Problem.

Hinweis:	Der Ausdruck Priority Queue ist insofern irreführend, als diese Datenstruktur keine Queue in dem Sinne darstellt, der in Überblick über Stack und Queue beschrieben wurde, denn er gibt Elemente nicht in einer strengen FIFO-Ordnung zurück. Trotzdem hat sich dieser Name für diesen Datentyp im Lauf der Zeit fest eingebürgert.

Ein dem Computer relativ nahes Beispiel für eine Priority Queue ist die Warteschlange, die ein Betriebssystem für die anstehenden Prozesse unterhält, wobei der mit jedem Element verbundene Wert die Priorität der Arbeit ist. Es kann beispielsweise erforderlich sein, schnell auf einen Tastendruck an einem Terminal zu reagieren, bevor die Daten beim nächsten Tastendruck verloren gehen. Andererseits kann das Kopieren eines Listings in die Druckerwarteschlange, in der schon andere Aufträge auf die Erledigung durch den Drucker warten, noch eine kurze Zeit verschoben werden, sofern es nur bei Gelegenheit wirklich ausgeführt wird. Dadurch, daß Prozesse in einer Priority Queue gehalten werden, werden die Aufgaben mit hoher Priorität vor den weniger dringenden Aufgaben ausgeführt.

Simulationsprogramme verwenden eine Priority Queue "zukünftiger Ereignisse". Die Simulation unterhält eine virtuelle "Uhr", und mit jedem Ereignis ist eine Ausführungszeit verbunden. In einer solchen Kollektion ist das Element mit dem kleinsten Zeitwert das Ereignis, das als nächstes ausgeführt werden sollte. Dies sind nur einige Beispiele für die Art von Problemstellung, in denen eine Priority Queue einen sinnvollen Ansatz darstellt. Wahrscheinlich sind Sie anderen Beispielen schon begegnet oder werden es in Zukunft tun.


Die Priority Queue-Operationen

Eine Priority Queue ist eine Datenstruktur, die Elemente vom Typ T enthalten kann und die folgenden fünf Operationen implementiert:

push(T)	Fügt der Kollektion einen neuen Wert hinzu

top()	Liefert eine Referenz auf das kleinste Element der Kollektion zurück

pop()	Löscht das kleinste Element in der Kollektion

size()	Gibt die Anzahl der Elemente in der Kollektion zurück

empty()	Gibt true zurück, wenn die Kollektion leer ist

Die Elemente vom Typ T müssen miteinander verglichen werden können, und zwar entweder mit dem Standardoperator Kleiner als (Operator <)oder mit einer Vergleichsfunktion. Diese kann entweder als Template-Argument oder als optionales Argument des Konstruktors übergeben werden. Diese zweite Form wird in dem Beispielprogramm weiter unten in diesem Abschnitt verwendet. Wie bei allen Containern in der Standardbibliothek gibt es zwei Konstruktoren. Der Standardkonstruktor benötigt entweder gar keine Argumente oder die optionale Vergleichsfunktion. Ein alternativer Konstruktor nimmt ein Iteratorenpaar an und initialisiert die Werte im Container aus der Argumentsequenz. Auch hier kann optional ein drittes Element für das Definieren der Vergleichsfunktion verwendet werden.

Hinweis:	Wie bereits in früheren Abschnitten ausgeführt, erfordert das Initialisieren von Containern unter Verwendung eines Iteratorenpaars ein Merkmal, das von vielen Compilern noch nicht unterstützt wird. Diese Konstruktorform ist hier zwar dokumentiert, sie steht auf Ihrem System aber möglicherweise noch nicht zur Verfügung.

Der Datentyp Priority Queue baut auf einer Container-Klasse auf, die die zum eigentlichen Speichern der Werte der Kollektion verwendete Struktur darstellt. In der Standardbibliothek gibt es zwei Container, die zum Konstruieren von Priority Queues verwendet werden können: Vektoren und Deques. Die folgenden Zeilen sollen die Deklarationen einiger Priority Queues veranschaulichen:

priority_queue< int, vector<int> > queue_one;

priority_queue< int, vector<int>, greater<int> > queue_two;

priority_queue< double, deque<double> >

queue_three(aList.begin(), aList.end());

priority_queue< eventStruct, vector<eventStruct> >

queue_four(eventComparison);

priority_queue< eventStruct, deque<eventStruct> >

queue_five(aVector.begin(), aVector.end(), eventComparison);

Queues, die aus Vektoren konstruiert werden, sind tendenziell etwas kleiner, während Queues, die auf Deques aufbauen, unter Umständen schneller ausgeführt werden, insbesondere dann, wenn die Anzahl der Elemente in der Queue zur Laufzeit stark schwankt. Diese Unterschiede sind jedoch gering, und in den meisten Fällen kann jede der beiden Formen verwendet werden.

Programme, die die Datenabstraktion Priority Queue verwenden, sollten die Datei queue einbinden sowie die Include-Datei für den Container-Typ (z.B. vector).

# include <queue>

# include <vector>

Die Datenstruktur Priority Queue weiß von sich aus nicht, wie Iteratoren konstruiert werden. Daher können nur sehr wenige der unter Überblick über generische Algorithmen beschriebenen Algorithmen in Verbindung mit Priority Queues verwendet werden. Statt durch Werte zu iterieren, konstruiert ein typischer Algorithmus für eine Priority Queue eine Schleife, die wiederholt Werte aus der Struktur zieht (mit den Operationen top() und pop()), bis die Kollektion leer ist (mit der Operation empty() getestet). Das im nächsten Abschnitt beschriebene Beispielprogramm macht dieses Verfahren deutlich.

Hinweis:	Einzelheiten der Algorithmen, die zum Bearbeiten von Heaps verwendet werden, werden hier nicht erläutert. Diese Informationen stehen aber in nahezu jedem Handbuch über Datenstrukturen zur Verfügung.

Priority Queues sind in der Form implementiert, daß intern eine Datenstruktur erzeugt wird, die man als Heap bezeichnet. Abstrakt gesehen ist ein Heap eine binäre Baumstruktur. Jeder Knoten in dieser Struktur besitzt die Eigenschaft, daß der ihm verbundene Wert kleiner als oder gleich groß wie der Wert ist, der zu einem jeden seiner untergeordneten Knoten gehört.


Anwendung: Ereignisgesteuerte Simulation

Die Verwendung der Datenstruktur Priority Queue soll an einem erweiterten Beispiel verdeutlicht werden. Das Beispiel beleuchtet eine der üblicheren Einsatzweisen von Priority Queues, nämlich die Unterstützung der Konstruktion eines Simulationsmodells.

Die diskrete ereignisgesteuerte Simulation ist ein verbreitetes Simulationsverfahren. Objekte in der Simulation bilden dabei Objekte in der wirklichen Welt nach, und sie sind so programmiert, daß ihr Verhalten dem der realen Objekte möglichst nahekommt. Eine Priority Queue wird verwendet, um eine Repräsentation von "Ereignissen" zu speichern, deren Eintreten ansteht. Diese Warteschlange wird geordnet gespeichert, und zwar auf der Grundlage der vorgesehenen Eintrittszeit des Ereignisses. Auf diese Weise ist also immer das kleinste Element das nächste nachzubildende Ereignis. Wenn ein Ereignis eintritt, kann es weitere Ereignisse als Unterprozesse absetzen. Diese nachfolgenden Ereignisse werden ebenfalls in die Warteschlange eingefügt. Die Ausführung wird fortgesetzt, bis alle Ereignisse bearbeitet worden sind.

Hinweis:	Die Priority Queue wurde als Struktur vorgestellt, die das schnellste Auffinden des größten Elements in einer Sequenz ermöglicht. Wenn Ihr Problem hingegen das Auffinden des kleinsten Elements erfordert, stehen verschiedene Möglichkeiten zur Wahl. Eine besteht darin, dem Konstruktor den Invertierungsoperator entweder als Template-Argument oder als optionales Vergleichsfunktionsargument zu übergeben. Wenn das Vergleichsargument als Funktion definiert wird, wie in dem Beispielproblem, besteht eine weitere Möglichkeit in der Umkehrung der Vergleichsprüfung.

Ereignisse können als Unterklassen einer Basisklasse dargestellt werden, die im Beispiel event heißt. Die Basisklasse hält einfach die Zeit fest, zu der das Ereignis eintritt. Eine abstrakte Funktion mit dem Namen processEvent wird aufgerufen, um das Ereignis auszuführen.

class event {

public:

event (unsigned int t) : time(t) { }

const unsigned int time;

virtual void processEvent() = 0;

};

Die Queue der Simulation muß eine Kollektion verschiedener Ereignistypen enthalten. Jede Ereignisform wird durch eine eigene Unterklasse der Klasse event dargestellt. Nicht alle Ereignisse werden also exakt dem gleichen Typ angehören, obwohl sie alle Unterklassen von event sind. (Dies wird auch als heterogene Kollektion bezeichnet.) Aus diesem Grund muß die Kollektion Zeiger auf Ereignisse anstelle der Ereignisse selbst speichern. (Theoretisch könnte man auch einfach Referenzen anstelle von Zeigern speichern, dies ist jedoch mit den Containern der Standardbibliothek nicht möglich).

Da der Vergleich von Zeigern nicht auf der Grundlage der Zeigertypen spezialisiert werden kann, muß statt dessen eine Vergleichsfunktion von Zeigern auf Ereignisse definiert werden. In der Standardbibliothek wird das durch Definieren einer neuen Struktur erreicht, deren einziger Zweck in einer Definition des Funktionsaufrufoperators (des Operators () ) in geeigneter Weise besteht. Da in diesem speziellen Beispiel die Priority Queue jeweils das kleinste Element - statt des größten - zurückliefern soll, wird die Reihenfolge des Vergleichs wie folgt umgekehrt:

struct eventComparison {

bool operator () (event * left, event * right) const

{ return left->time > right->time; }

};

An diesem Punkt kann nun die Klasse simulation definiert werden, die die Struktur für die Simulationsaktivitäten zur Verfügung stellt. Die Klasse simulation verfügt über zwei Funktionen. Die erste wird verwendet, um neue Ereignisse in die Queue einzufügen, während die zweite die Simulation ausführt. Es steht ferner ein Datenfeld zur Verfügung, das die aktuelle Simulations-"Zeit" enthält.

Hinweis:	Die bisher vorgestellten Beispielprogramme haben Container immer zum Speichern von Werten verwendet. In diesem Beispiel enthält der Container Zeiger auf Werte, nicht aber die Werte selbst. Eine Folge dieses Umstands ist, daß die Verantwortung für die Speicherverwaltung der bearbeiteten Objekte beim Programmierer liegt.

class simulation {

public:

simulation () : eventQueue(), time(0) { }

void scheduleEvent (event * newEvent)

{ eventQueue.push (newEvent); }

void run();

unsigned int time;

protected:

priority_queue<event *, vector<event *>, eventComparison> eventQueue;

};

Beachten Sie die Deklaration der Priority Queue, die die anstehenden Ereignisse enthält. In diesem Fall wird ein Vektor als zugrundeliegender Container verwendet. Ebensogut hätte aber auch ein Deque eingesetzt werden können.

Das Kernstück der ereignisgesteuerten Simulation ist die Elementfunktion run(), die die Ereignisschleife definiert. Diese Prozedur macht von drei der fünf Operationen von Priority Queues Gebrauch, nämlich top(), pop()und empty(). Sie ist wie folgt implementiert:

void simulation::run()

{

while (! eventQueue.empty()) {

event * nextEvent = eventQueue.top();

eventQueue.pop();

time = nextEvent->time;

nextEvent->processEvent();

delete nextEvent;	// von Ereignis benötigten Speicher freigeben

}

}


Eine Eiscafé-Simulation

Hinweis:	Die vollständige Ereignissimulation befindet sich in der Datei icecream.cpp auf dem mitgelieferten Datenträger.

Die Verwendung unseres Simulations-Frameworks soll an diesem Beispiel verdeutlicht werden, einer einfachen Simulation eines Eiscafés. Eine derartige Simulation könnte beispielsweise verwendet werden, um die optimale Anzahl Stühle, die aufgestellt werden sollten, zu ermitteln, ausgehend von Annahmen wie der Häufigkeit des Eintreffens neuer Kunden, ihrer Aufenthaltsdauer und so weiter.

Unsere Simulation eines Eiscafés baut auf einer Unterklasse der Klasse simulation auf, die wie folgt definiert ist:

class storeSimulation : public simulation {

public:

storeSimulation()

: freeChairs(35), profit(0.0), simulation() { }

bool canSeat (unsigned int numberOfPeople);

void order(unsigned int numberOfScoops);

void leave(unsigned int numberOfPeople);

private:

unsigned int freeChairs;

double profit;

} theSimulation;

Drei Grundaktivitäten sind mit dem Eiscafé verbunden. Diese sind Eintreffen, Bestellung und Verzehr sowie Verlassen des Eiscafés. Dies spiegelt sich nicht nur in den drei Elementfunktionen wieder, die in der Klasse simulation definiert sind, sondern außerdem in drei separaten Unterklassen von event.

Die Elementfunktionen, die mit dem Eiscafé verbunden sind, zeichnen einfach die stattfindenden Aktivitäten auf und erzeugen ein Protokoll, das später zur Auswertung der Simulation untersucht werden kann.

bool storeSimulation::canSeat (unsigned int numberOfPeople)

// wenn Platz ausreicht, Kunden auf Sitzplätze verteilen

{

cout << "Time: " << time;

cout << " group of " << numberOfPeople << " customers arrives";

if (numberOfPeople < freeChairs) {

cout << " is seated" << endl;

freeChairs -= numberOfPeople;

return true;

}

else {

cout << " no room, they leave" << endl;

return false;

}

}

void storeSimulation::order (unsigned int numberOfScoops)

// Eis servieren, Profit berechnen

{

cout << "Time: " << time;

cout << " serviced order for " << numberOfScoops << endl;

profit += 0.35 * numberOfScoops;

}

void storeSimulation::leave (unsigned int numberOfPeople)

// Kunden gehen, Stühle werden freigegeben

{

cout << "Time: " << time;

cout << " group of size " << numberOfPeople <<

" leaves" << endl;

freeChairs += numberOfPeople;

}

Wie schon vorher ausgeführt, entspricht jeder Aktivität eine eigene Ereignisunterklasse. Jede Unterklasse von event enthält ein Integer-Datenfeld, das die Größe einer Kundengruppe darstellt. Das Ereignis Ankunft findet statt, wenn eine Gruppe eintritt. Bei der Ausführung erzeugt und installiert das Ereignis Ankunft eine neue Instanz des Ereignisses Bestellung (order). Die Funktion randomInteger() (siehe Random Access-Iteratoren) wird verwendet, um eine zufällige Ganzzahl zwischen 1 und dem Argumentwert zu berechnen.

class arriveEvent : public event {

public:

arriveEvent (unsigned int time, unsigned int groupSize)

: event(time), size(groupSize) { }

virtual void processEvent ();

private:

unsigned int size;

};

void arriveEvent::processEvent()

{	// prüfen, ob genug Sitzplätze zur Verfügung stehen

if (theSimulation.canSeat(size))

theSimulation.scheduleEvent

(new orderEvent(time + 1 + randomInteger(4), size));

}

Ein Ereignis Bestellung setzt in ähnlicher Weise ein Ereignis Café Verlassen ab.

class orderEvent : public event {

public:

orderEvent (unsigned int time, unsigned int groupSize)

: event(time), size(groupSize) { }

virtual void processEvent ();

private:

unsigned int size;

};

void orderEvent::processEvent()

{	// jeder Kunde bestellt eine Anzahl Kugeln

for (int i = 0; i < size; i++)

theSimulation.order(1 + rand(3));

theSimulation.scheduleEvent

(new leaveEvent(time + 1 + randomInteger(10), size));

};

Ereignisse Verlassen schließlich geben Stühle frei, lösen aber keine neuen Ereignisse aus.

class leaveEvent : public event {

public:

leaveEvent (unsigned int time, unsigned int groupSize)

: event(time), size(groupSize) { }

virtual void processEvent ();

private:

unsigned int size;

};

void leaveEvent::processEvent ()

{	// Verlassen und Stühle freigeben

theSimulation.leave(size);

}

Zum Ausführen der Simulation erzeugen wir einfach eine bestimmte Zahl Anfangsereignisse ( z.B. äquivalent 30 Minuten Echtzeit) und rufen die Elementfunktion run()auf.

void main() {

// Queue mit einer bestimmten Anzahl Anfangsereignisse laden

unsigned int t = 0;

while (t < 30) {

t += rand(6);

theSimulation.scheduleEvent(

new arriveEvent(t, 1 + randomInteger(4)));

}

// dann Simulation ausführen und Profit ausweisen

theSimulation.run();

cout << "Total profits " << theSimulation.profit << endl;

}


Generische Algorithmen

12.1 Generische Algorithmen: Überblick

12.2 Initialisierungsalgorithmen

12.3 Suchalgorithmen

12.4 Stationäre Transformationen

12.5 Entfernungsalgorithmen

12.6 Skalarergebnis-Algorithmen

12.7 Sequenzerzeugende Algorithmen

12.8 Verschiedene Algorithmen


Generische Algorithmen: Überblick

Dieser Abschnitt und Abschnitt 11 untersuchen und erläutern die einzelnen generischen Algorithmen,  die die Standardbibliothek zur Verfügung stellt. Die folgende Tabelle enthält die Namen sowie eine kurze Beschreibung aller Algorithmen, die in diesem Abschnitt behandelt werden. Die Algorithmen wurden ausgehend von ihrer typischen Verwendung verschiedenen Kategorien zugeordnet. Die hier vorgenommene Zuordnung unterscheidet sich von den Kategorien der C++ Standarddefinition, die als Kriterium verwendet, ob Algorithmen ihre Argumente verändern oder nicht.

Name	Verwendung

Algorithmen zur Initialisierung einer Sequenz

fill	füllt eine Sequenz mit einem Initialwert
fill_n	füllt n Positionen mit einem Initialwert
copy	kopiert eine Sequenz in eine andere Sequenz
copy_backward	kopiert eine Sequenz in eine andere Sequenz
generate	initialisiert eine Sequenz durch einen Generator
generate_n	initialisiert n Positionen durch einen Generator
swap_ranges	tauscht Werte aus zwei parallelen Sequenzen aus
Suchalgorithmen

find	sucht ein Element, das dem Argument entspricht
find_if	sucht ein Element, das einer Bedingung genügt
adjacent_find	sucht aufeinanderfolgende, doppelte Elemente
search	sucht nach einer Untersequenz innerhalb einer Sequenz
max_element	sucht den Maximalwert in einer Sequenz
min_element	sucht den Minimalwert in einer Sequenz
mismatch	sucht die erste Nichtentsprechung in parallelen Sequenzen
Stationäre Transformationen

reverse	kehrt die Elemente in einer Sequenz um
replace	ersetzt bestimmte Werte durch einen neuen Wert
replace_if	ersetzt die Elemente, die dem Prädikat entsprechen
rotate	rotiert die Elemente in einer Sequenz um einen Punkt
partition	teilt die Elemente in zwei Gruppen auf
stable_partition	teilt unter Beibehaltung der ursprünglichen Ordnung auf
next_permutation	erzeugt eine Permutationenfolge
prev_permutation	erzeugt Permutationen in umgekehrter Reihenfolge
inplace_merge	führt zwei benachbarte Streams in einen Stream zusammen
random_shuffle	ordnet die Elemente einer Sequenz in zufällige Folge
Entfernungsalgorithmen

remove	entfernt die Elemente, die einer Bedingung genügen
unique	entfernt alle doppelten Werte außer dem ersten Wert in einer Sequenz
Skalarergebnis-Algorithmen

count	zählt die Elemente, die dem Wert entsprechen
count_if	zählt die Elemente, die dem Prädikat entsprechen
accumulate	reduziert die Sequenz auf einen Skalarwert
inner_product	inneres Produkt zweier paralleler Sequenzen
equal	überprüft zwei Sequenzen auf Gleichheit
lexicographical_compare	vergleicht zwei Sequenzen
Sequenzerzeugende Algorithmen

transform	wandelt jedes Element um
partial_sum	erzeugt Sequenz von Teilsummen
adjacent_difference	erzeugt Sequenz benachbarter Differenzen
Verschiedene Operationen

for_each	wendet eine Funktion auf jedes Element in einer Kollektion an
Um einen der generischen Algorithmen zu verwenden, müssen Sie zuerst die entsprechende Header-Datei einbinden. Die meisten der Funktionen sind in der Header-Datei algorithm definiert. Die Funktionen accumulate(), inner_product(), partial_sum(), und adjacent_difference() sind in der Header-Datei numeric definiert.

# include <algorithm>

# include <numeric>

In diesem Abschnitt wird die Verwendung jedes Algorithmus durch eine Reihe kurzer Beispiele erklärt. Viele der Algorithmen werden auch in den Beispielprogrammen verwendet, die Sie in den Abschnitten zu den verschiedenen Container-Klassen finden. Querverweise finden Sie an den entsprechenden Stellen.

Alle in diesem Abschnitt enthaltenen Beispielprogramme wurden in Dateien mit den Namen alg1.cpp bis alg6.cpp zusammengefaßt. Die Dateiversion der Beispielprogramme wurde um Ausgabeanweisungen erweitert, die die Testprogramme beschreiben und das Ergebnis der Ausführung der Algorithmen illustrieren. Um nicht durch unnötige Einzelheiten zu verwirren, sind diese Ausgabeanweisungen in der vorliegenden Beschreibung nicht enthalten. Wenn Sie sich die vollständigen Beispielprogramme mit den Ausgabeanweisungen ansehen möchten, können Sie die Testdateien compilieren und ausführen. Die zu erwartende Ausgabe dieser Programme ist ebenfalls enthalten.


Initialisierungsalgorithmen

Hinweis:	Die in diesem Abschnitt vorgestellten Beispielprogramme finden Sie in der Datei alg1.cpp.

Bei der ersten Gruppe von Algorithmen, die hier beschrieben wird, handelt es sich um Algorithmen, die hauptsächlich, jedoch nicht ausschließlich, zum Initialisieren einer neu erzeugten Sequenz mit bestimmten Werten verwendet werden. Die Standardbibliothek stellt mehrere Initialisierungsalgorithmen zur Verfügung. Die folgenden Ausführungen enthalten Beispiele für die Verwendung dieser Algorithmen sowie Hinweise zur Auswahl des geeigneten Algorithmus.


Füllen einer Sequenz mit einem Initialwert

Die Algorithmen fill() und fill_n() werden dazu verwendet, eine Sequenz mit einem festen Wert zu initialisieren oder erneut zu initialisieren. Ihre Definition ist folgendermaßen:

void fill (ForwardIterator first, ForwardIterator last, const T&);

void fill_n (OutputIterator, Size, const T&);

Hinweis:	Alle Initialisierungsalgorithmen überschreiben sämtliche Elemente in einem Container. Der Unterschied zwischen den verschiedenen Algorithmen liegt in der Quelle, aus der die zur Initialisierung verwendeten Werte stammen. Der Algorithmus fill() wiederholt einen einzelnen Wert, der Algorithmus copy() liest Werte aus einem zweiten Container, und der Algorithmus generate() ruft für jeden neuen Wert eine Funktion auf.

Das Beispielprogramm zeigt verschiedene Verwendungen des Algorithmus:

void fill_example ()

// Verwendung des Füllalgorithmus

{

// Beispiel 1, Füllen eines Arrays mit Initialwerten

char buffer[100], * bufferp = buffer;

fill (bufferp, bufferp + 100, '\0');

fill_n (bufferp, 10, 'x');

// Beispiel 2, Verwendung von Füllen zur Initialisierung einer Liste

list<string> aList(5, "nothing");

fill_n (inserter(aList, aList.begin()), 10, "empty");

// Beispiel 3, Verwendung von Füllen zum Überschreiben von Werten in einer Liste

fill (aList.begin(), aList.end(), "full");

// Beispiel 4, Füllen eines Teils einer Kollektion

vector<int> iVec(10);

generate (iVec.begin(), iVec.end(), iotaGen(1));

vector<int>::iterator & seven =

find(iVec.begin(), iVec.end(), 7);

fill (iVec.begin(), seven, 0);

}

In Beispiel 1 wird ein Array von Zeichenwerten deklariert. Der Algorithmus fill() wird aufgerufen, um jedes Element des Arrays mit dem Zeichenwert Null zu initialisieren. Die ersten 10 Positionen werden anschließend unter Verwendung des Algorithmus fill_n() durch das Zeichen 'x' ersetzt. Beachten Sie, daß der Algorithmus fill() sowohl Anfangs- als auch Nach-Bereichsendeiteratoren als Argumente benötigt. Der Algorithmus fill_n() hingegen arbeitet mit einem Anfangsiterator und einen Zähler.

Beispiel 2 zeigt, wie der Algorithmus fill_n() durch die Verwendung eines Einfügeiterators (vgl. Einfügeiteratoren) für die Initialisierung eines Containers variabler Länge (beispielsweise einer Liste) verwendet werden kann. Im Beispiel besteht die Liste zu Beginn aus fünf Elementen, die den Text "nothing" enthalten. Der Aufruf von fill_n() fügt anschließend 10 Instanzen des Strings "empty" ein. Die sich ergebende Liste enthält 15 Elemente.

Das dritte und vierte Beispiel zeigen, wie fill() verwendet werden kann, um die Werte in einem vorhandenen Container zu ändern. Im dritten Beispiel wird jedes der 15 Elemente der Liste, die in Beispiel 2 erzeugt wurde, durch den String "full" ersetzt.

Beispiel 4 überschreibt nur einen Teil einer Liste. Der Algorithmus generate() und das Funktionsobjekt iotaGen, das im nächsten Abschnitt beschrieben wird, werden verwendet, um einen Vektor mit den Werten 1 2 3 ... 10 zu initialisieren. Der Algorithmus find() (Sucht ein Element, das einer Bedingung genügt) wird anschließend verwendet, um die Position des Elements 7 herauszufinden. Diese Position wird in einem dem Vektordatentyp entsprechenden Iterator gespeichert. Durch den Aufruf von fill() werden anschließend alle Werte bis zum Eintrag 7 durch den Wert 0 ersetzt. Eintrag 7 selbst wird nicht ersetzt. Der resultierende Vektor besitzt 6 Nullfelder, gefolgt von den Werten 7, 8, 9 und 10.

Die Algorithmen fill() und fill_n() können mit allen Container-Klassen der Standardbibliothek verwendet werden. Einfügeoperatoren müssen jedoch mit geordneten Containern, beispielsweise Sets, verwendet werden.


Kopieren einer Sequenz in eine andere Sequenz

Die Algorithmen copy() und copy_backward() sind vielseitig einsetzbare Funktionen, die für eine Reihe verschiedener Zwecke verwendet werden können. Wahrscheinlich handelt es sich um die am häufigsten ausgeführten Algorithmen der Standardbibliothek. Die Definitionen dieser Algorithmen sind folgendermaßen:

OutputIterator copy (InputIterator first, InputIterator last,

OutputIterator result);

BidirectionalIterator copy_backward

(BidirectionalIterator first, BidirectionalIterator last,

BidirectionalIterator result);

Hinweis:	Das Ergebnis, das die Funktion copy() zurückliefert, ist ein Zeiger auf das Ende der kopierten Sequenz. Um eine Verkettung von Werten zu erhalten, kann das Ergebnis einer Operation copy() als Anfangsiterator einer folgenden Operation copy() verwendet werden.

Einsatzmöglichkeiten des Kopieralgorithmus sind beispielsweise:

Duplizieren einer ganzen Sequenz durch Kopieren in eine neue Sequenz

Erzeugen von Untersequenzen einer vorhandenen Sequenz

Hinzufügen von Elementen zu einer Sequenz

Kopieren einer Sequenz aus der Eingabe oder in die Ausgabe

Umwandeln einer Sequenz aus einer Form in eine andere

Das folgende Beispielprogramm zeigt diese Verwendungsarten.

void copy_example()

// Beispiel für die Verwendung des Kopieralgorithmus

{

char * source = "reprise";

char * surpass = "surpass";

char buffer[120], * bufferp = buffer;

// Beispiel 1, ein einfacher Kopiervorgang

copy (source, source + strlen(source) + 1, bufferp);

// Beispiel 2, Selbstkopien

copy (bufferp + 2, bufferp + strlen(buffer) + 1, bufferp);

int buflen = strlen(buffer) + 1;

copy_backward (bufferp, bufferp + buflen, bufferp + buflen + 3);

copy (surpass, surpass + 3, bufferp);

// Beispiel 3, Kopieren in die Ausgabe

copy (bufferp, bufferp + strlen(buffer),

ostream_iterator<char>(cout));

cout << endl;

// Beispiel 4, Verwenden von copy, um den Typ umzuwandeln

list<char> char_list;

copy (bufferp, bufferp + strlen(buffer),

inserter(char_list, char_list.end()));

char * big = "big ";

copy (big, big + 4, inserter(char_list, char_list.begin()));

char buffer2 [120], * buffer2p = buffer2;

* copy (char_list.begin(), char_list.end(), buffer2p) = '\0';

cout << buffer2 << endl;

}

Der erste Aufruf von copy()in Beispiel 1 kopiert einfach den String, auf den die Variable source zeigt, in einen Puffer. Als Ergebnis dieses Vorgangs enthält der Puffer den Text "reprise". Beachten Sie, daß die Endposition des Kopiervorgangs ein Element nach dem abschließenden Nullzeichen ist. Hierdurch wird sichergestellt, daß auch das Nullzeichen in die Kopieroperation eingeschlossen wird.

Die Operation copy() ermöglicht insbesondere auch Selbstkopien, d. h., eine Sequenz kann in sich selbst kopiert werden, solange der Zieloperator nicht in den Bereich fällt, den die Quelliteratoren bilden. Beispiel 2 illustriert diesen Fall. Der Kopiervorgang beginnt an Position 2 des Puffers und reicht bis zum Ende. Die Zeichen werden an den Anfang des Puffers kopiert. Als Ergebnis enthält der Puffer den Wert "prise".

Die zweite Hälfte von Beispiel 2 zeigt die Verwendung des Algorithmus copy_backward(). Diese Funktion führt die gleiche Aufgabe wie der Algorithmus copy() aus, verschiebt jedoch Elemente vom Ende der Sequenz zuerst und schreitet zum vorderen Teil der Sequenz vor. (Wenn Sie das Argument als String betrachten, werden Zeichen von rechts nach links verschoben.) Dem Puffer wird in diesem Fall der Wert "priprise" zugewiesen. Die ersten drei Zeichen werden anschließend durch eine weitere Operation copy() auf die Werte "sur" geändert. Als Ergebnis enthält der Puffer den Wert "surprise".

Hinweis:	Beachten Sie beim Algorithmus copy_backwards, daß sich "rückwärts" auf die Übertragungsreihenfolge und nicht auf die Elemente selbst bezieht. Die relative Anordnung der verschobenen Werte im Ziel ist die gleiche wie in der Quelle.

Beispiel 3 zeigt, wie Werte mit copy() in einen Ausgabe-Stream verschoben werden (vgl. Ausgabe-Stream-Iteratoren). Das Ziel ist in diesem Fall ein ostream_iterator, der für den Ausgabe-Stream cout erzeugt wurde. Ein ähnliches Verfahren kann für Eingabewerte verwendet werden. Eine einfache Methode, jedes Wort im Eingabe-Stream in eine Liste zu kopieren, stellt der folgende Aufruf von copy()dar:

list<string> words;

istream_iterator<string, ptrdiff_t> in_stream(cin), eof;

copy(in_stream, eof, inserter(words, words.begin()));

Dieses Verfahren wird in dem Programm zur Rechtschreibprüfung, das Sie unter Beispielprogramm: Rechtschreibprüfung finden, verwendet.

Sie können über Kopieren auch einen Typ von Stream in einen anderen umwandeln. Der Aufruf in Beispiel 4 kopiert die im Puffer enthaltenen Zeichen nacheinander in eine Zeichenliste. Der Aufruf von inserter() erzeugt einen Einfügeiterator, der zum Einfügen von Werten in die Liste verwendet wird. Der erste Aufruf von copy() überträgt den in Beispiel 2 erzeugten String surprise in die Liste. Der zweite Aufruf copy() fügt die Werte aus dem String "big " vorn in der Liste ein. Als Ergebnis enthält die Liste die Zeichen big surprise. Der letzte Aufruf von copy() zeigt das umgekehrte Verfahren: Zeichen werden aus einer Liste zurück in einen Zeichenpuffer kopiert.


Initialisieren einer Sequenz mit generierten Werten

Ein Generator ist eine Funktion, die bei aufeinanderfolgenden Aufrufen eine Serie von Werten zurückliefert. Der bekannteste Generator ist vielleicht ein Zufallszahlengenerator. Generatoren können jedoch für ganz unterschiedliche Zwecke, z.B. auch für die Initialisierung von Sequenzen, konstruiert werden.

Ebenso wie fill() und fill_n()werden die Algorithmen generate() und generate_n() verwendet, um eine Sequenz zu initialisieren oder neu zu initialisieren. Anstelle eines festen Arguments verwenden diese Algorithmen jedoch Werte aus einem Generator. Die Definition dieser Algorithmen ist folgendermaßen:

void generate (ForwardIterator, ForwardIterator, Generator);

void generate_n (OutputIterator, Size, Generator);

Das Beispielprogramm veranschaulicht verschiedene Möglichkeiten, mit dem Algorithmus generate eine Sequenz zu initialisieren.

string generateLabel () {

// Generieren eines eindeutigen Label-Strings der Form L_ddd

static int lastLabel = 0;

char labelBuffer[80];

ostrstream ost(labelBuffer, 80);

ost << "L_" << lastLabel++ << '\0';

return string(labelBuffer);

}

void generate_example ()

// Verwendung der Algorithmen generate und generate_n

{

// Beispiel 1, Generieren einer Liste von Label-Werten

list<string> labelList;

generate_n (inserter(labelList, labelList.begin()),

4, generateLabel);

// Beispiel 2, Generieren einer arithmetischen Reihe

vector<int> iVec(10);

generate (iVec.begin(), iVec.end(), iotaGen(2));

generate_n (iVec.begin(), 5, iotaGen(7));

}

Ein Generator kann als einfache Funktion erzeugt werden, die Informationen ihrer früheren Zustände in einer oder mehreren statischen Variablen speichert. Ein Beispiel hierfür finden Sie am Anfang des Beispielprogramms unter der Beschreibung der Funktion generateLabel(). Diese Funktion erzeugt eine Sequenz eindeutiger String-Labels, wie sie eventuell von einem Compiler benötigt werden. Jeder Aufruf der Funktion generateLabel() führt zu einem neuen String der Form L_ddd, und jeder String besitzt einen eindeutigen Zahlenwert. Da die Variable namens lastLabel als statisch deklariert ist, wird ihr Wert von einem Aufruf bis zum nächsten gespeichert. Das erste Beispiel des Beispielprogramms führt vor, wie diese Funktion zusammen mit dem Algorithmus generate_n() verwendet werden kann, um eine Liste aus vier Label-Werten zu initialisieren.

Wie unter Funktionen erläutert, stellt eine Funktion in der Standardbibliothek ein beliebiges Objekt dar, das auf den Funktionsaufrufoperator reagiert. Unter Berücksichtigung dieser Tatsache können Klassen auf einfache Art als Funktionen erzeugt werden. Die Klasse iotaGen, die unter Funktionsobjekte beschrieben wurde, ist ein Beispiel hierfür. Das Funktionsobjekt iotaGen erzeugt einen Generator für eine arithmetische Integerreihe. Im zweiten Beispiel des Beispielprogramms wird diese Sequenz verwendet, um einen Vektor mit den Integerwerten 2 bis 11 zu initialisieren. Ein Aufruf von generate_n() wird anschließend verwendet, um die ersten fünf Positionen des Vektors mit den Werten von 7 bis 11 zu überschreiben. Das Ergebnis ist der Vektor 7 8 9 10 11 7 8 9 10 11.


Austauschen von Werten zwischen zwei parallelen Bereichen

Die Template-Funktion swap() kann verwendet werden, um die Werte zweier Objekte gleichen Typs auszutauschen. Sie wird wie folgt definiert:

template <class T> void swap (T& a, T& b)

{

T temp(a);

a = b;

b = temp;

}

Die Funktion wird in der Funktion namens iter_swap()zu Iteratoren generalisiert. Der Algorithmus swap_ranges() erweitert diese anschließend zu ganzen Sequenzen. Die durch die erste Sequenz bezeichneten Werte werden mit den durch eine zweite, parallele Sequenz bezeichneten Werte ausgetauscht. Die Beschreibung des Algorithmus swap_ranges() ist folgendermaßen:

ForwardIterator swap_ranges

(ForwardIterator first, ForwardIterator last,

ForwardIterator first2);

Hinweis:	Es gibt mehrere Algorithmen, die mit zwei parallelen Sequenzen arbeiten. In den meisten Fällen wird die zweite Sequenz nur durch einen Anfangsiterator gekennzeichnet, nicht durch ein Anfangs- und Enditeratorenpaar. Es wird davon ausgegangen (nicht jedoch überprüft), daß die zweite Sequenz mindestens so groß wie die erste Sequenz ist. Wird dieser Bedingung nicht genügt, treten Fehler auf.

Der zweite Bereich wird nur durch einen Anfangsiterator gekennzeichnet. Es wird davon ausgegangen (nicht jedoch überprüft), daß der zweite Bereich mindestens so viele Elemente besitzt wie der erste Bereich. Das Beispielprogramm verwendet beide Funktionen allein oder in Kombination.

void swap_example ()

// Verwendung des Algorithmus swap_ranges

{

// Erzeugen von zwei parallelen Sequenzen

int data[] = {12, 27, 14, 64}, *datap = data;

vector<int> aVec(4);

generate(aVec.begin(), aVec.end(), iotaGen(1));

// Verwendung von swap und iter_swap

swap(data[0], data[2]);

vector<int>::iterator last = aVec.end(); last--;

iter_swap(aVec.begin(), last);

// Austauschen der gesamten Sequenz

swap_ranges (aVec.begin(), aVec.end(), datap);

}


Suchoperationen

Bei der nächsten Kategorie von Algorithmen, die erläutert wird, handelt es sich um Algorithmen, die zum Suchen von Elementen, die bestimmten Eigenschaften genügen, innerhalb einer Sequenz verwendet werden. Normalerweise dient das Ergebnis der Suche anschließend als Argument einer weiteren Operation, beispielsweise der Operation partition (Aufteilen einer Sequenz in zwei Gruppen), copy (Kopieren einer Sequenz in eine andere Sequenz) oder in-place merge (Zusammenführen zweier benachbarter Sequenzen in eine Sequenz).

Hinweis:	Die in diesem Abschnitt beschriebenen Beispielfunktionen finden Sie in der Datei alg2.cpp.

Die in diesem Abschnitt beschriebenen Suchroutinen liefern einen Iterator zurück, der das erste Element angibt, das der Suchbedingung genügt. Es ist üblich, diesen Wert wie folgt in einer Iteratorvariablen zu speichern:

list<int>::iterator where;

where = find(aList.begin(), aList.end(), 7);

Wenn Sie alle Elemente finden möchten, die den Suchbedingungen genügen, müssen Sie eine Schleife verwenden. In dieser Schleife wird zuerst der durch eine vorhergegangene Suche gelieferte Wert iteriert (da andernfalls der durch die vorherige Suche gelieferte Wert nochmals zurückgegeben würde), und der sich ergebende Wert wird als Ausgangspunkt für die neue Suche verwendet. Die folgende Schleife aus dem Beispielprogramm adjacent_find() (Suchen aufeinanderfolgender, doppelter Elemente) gibt den Wert aller Zeichen aus, die in einem String-Argument wiederholt werden.

Hinweis:	Die Suchalgorithmen in der Standardbibliothek geben alle den Sequenzendeiterator zurück, wenn kein der Suchbedingung entsprechender Wert gefunden wurde. Da es im allgemeinen nicht zulässig ist, den Sequenzendeiterator zu dereferenzieren, ist es sehr wichtig, diese Bedingung zu überprüfen, bevor das Ergebnis einer Suche weiterverarbeitet wird.

while ((where = adjacent_find(where, stop)) != stop) {

cout << "double " << *where << " in position "

<< where - start << endl;

++where;

}

Viele der Suchalgorithmen besitzen ein optionales Argument, das anstelle des Gleichheitsoperators für den Container-Elementtyp (Operator ==) eine für den Vergleich der Elemente zu verwendende Funktion angeben kann. In der Beschreibung der Algorithmen werden diese optionalen Argumente in eckigen Klammern angegeben. Sie müssen nicht angegeben werden, wenn der Standard-Gleichheitszeichenoperator verwendet werden kann.


Suchen eines Elements, das einer Bedingung genügt

Zwei Algorithmen, find() und find_if(), werden verwendet, um das erste Element zu suchen, das einer Bedingung genügt. Die Definitionen dieser beiden Algorithmen lauten folgendermaßen:

InputIterator find_if (InputIterator first, InputIterator last,

Predicate);

InputIterator find (InputIterator first, InputIterator last,

const T&);

Der Algorithmus find_if() nimmt als Argument eine Prädikatfunktion an, bei der es sich um eine beliebige Funktion handeln kann, die einen booleschen Wert (vgl. Prädikate) zurückliefert. Der Algorithmus find_if() gibt einen neuen Iterator zurück, der das erste Element der Sequenz bestimmt, das dem Prädikat genügt. Das zweite Argument, der Nach-Bereichsendeiterator, wird zurückgegeben, wenn kein Element gefunden wurde, das der Forderung entspricht. Da es sich bei dem Ergebniswert um einen Iterator handelt, muß der Dereferenzierungsoperator (der Operator *) verwendet werden, um den passenden Wert zu erhalten. Das Beispielprogramm zeigt dieses Verfahren.

Die zweite Form des Algorithmus, find(), ersetzt die Prädikatfunktion durch einen bestimmten Wert und liefert das erste Element der Sequenz zurück, das diesem Wert entspricht. Dabei wird der passende Gleichheitsoperator (der Operator ==) für den entsprechenden Datentyp verwendet.

Hinweis:	Diese Algorithmen führen eine lineare, sequentielle Suche durch die verbundenen Strukturen durch. Die geordneten Datenstrukturen set und map verfügen über eigene Elementfunktionen find(), die effizienter sind. Aus diesem Grund sollte der allgemeine Algorithmus find() nicht mit set und map verwendet werden.

Das folgende Beispielprogramm zeigt die Verwendung dieser Algorithmen:

void find_test ()

// Verwendung des Algorithmus find

{

int vintageYears[] = {1967, 1972, 1974, 1980, 1995};

int * start = vintageYears;

int * stop = start + 5;

int * where = find_if (start, stop, isLeapYear);

if (where != stop)

cout << "first vintage leap year is " << *where << endl;

else

cout << "no vintage leap years" << endl;

where = find(start, stop, 1995);

if (where != stop)

cout << "1995 is position " << where - start

<< " in sequence" << endl;

else

cout "1995 does not occur in sequence" << endl;

}


Suchen aufeinanderfolgender, doppelter Elemente

Der Algorithmus adjacent_find() wird verwendet, um das erste Element einer Sequenz zu finden, das gleich dem unmittelbar folgenden Element ist. Wenn eine Sequenz zum Beispiel die Werte 1 4 2 5 6 6 7 5 enthält, liefert der Algorithmus einen Iterator zurück, der dem ersten Wert 6 entspricht. Wird kein Wert gefunden, der der Bedingung genügt, wird der Sequenzendeiterator zurückgeliefert. Die Definition des Algorithmus ist folgendermaßen:

ForwardIterator adjacent_find (ForwardIterator first,

ForwardIterator last [, BinaryPredicate ] );

Die ersten beiden Argumente geben die zu untersuchende Sequenz an. Das optionale dritte Argument muß ein binäres Prädikat sein (eine binäre Funktion, die einen booleschen Wert zurückliefert). Wenn sie verwendet wird, dient die binäre Funktion dazu, benachbarte Elemente zu prüfen. Andernfalls wird der Gleichheitszeichenoperator (Operator ==) verwendet.

Das Beispielprogramm durchsucht einen Text-String nach benachbarten gleichen Buchstaben. Im Beispieltext werden diese an den Positionen 5, 7, 9, 21 und 37 gefunden. In der Schleife muß der Wert inkrementiert werden, da sonst immer wieder die gleiche Position gefunden wird.

void adjacent_find_example ()

// Verwendung der Anweisung adjacent_find

{

char * text = "The bookkeeper carefully opened the door.";

char * start = text;

char * stop = text + strlen(text);

char * where = start;

cout << "In the text: " << text << endl;

while ((where = adjacent_find(where, stop)) != stop) {

cout << "double " << *where

<< " in position " << where - start << endl;

++where;

}

}


Suchen einer Untersequenz innerhalb einer Sequenz

Der Algorithmus search() wird verwendet, um den Anfang einer bestimmten Untersequenz innerhalb einer größeren Sequenz zu finden. Ein einfaches Beispiel stellt die Suche nach einem bestimmten Unter-String innerhalb eines größeren Strings dar. Der Algorithmus kann jedoch auch für andere Anwendungen verallgemeinert werden. Es wird unterstellt, daß die Argumente mindestens die Eigenschaften von Vorwärtsiteratoren besitzen.

ForwardIterator search

(ForwardIterator first1, ForwardIterator last1,

ForwardIterator first2, ForwardIterator last2

[, BinaryPredicate ]);

Hinweis:	Im ungünstigsten Fall ist die Anzahl der Vergleiche, die der Algorithmus search() durchführt, das Produkt aus der Anzahl der Elemente in den beiden Sequenzen. Dies ist jedoch sehr unwahrscheinlich und tritt nur selten ein.

Angenommen, Sie möchten die Position des Strings "ration" im String "dreams and aspirations" herausfinden. Das Beispielprogramm zeigt die Lösung dieses Problems. Wenn keine Entsprechung gefunden wird, ist der Rückgabewert der Nach-Bereichsendeiterator für die erste Sequenz.

void search_example ()

// Verwendung des Suchalgorithmus

{

char * base = "dreams and aspirations";

char * text = "ration";

char * where = search(base, base + strlen(base),

text, text + strlen(text));

if (*where != '\0')

cout << "substring position: " << where = base << endl;

else

cout << "substring does not occur in text" << endl;

}

Beachten Sie, daß dieser Algorithmus, im Gegensatz zu vielen Algorithmen, die zwei Sequenzen manipulieren, ein Anfangs- und ein Enditeratorenpaar für beide Sequenzen (nicht nur für die erste Sequenz) verwendet.

Genau wie die Algorithmen equal() und mismatch(), nimmt auch eine alternative Version von search() ein optionales binäres Prädikat an, das zum Vergleichen von Elementen aus den beiden Sequenzen verwendet wird.


Suchen des größten oder kleinsten Elements

Die Funktionen max() und min() können verwendet werden, um das Maximum und Minimum eines Wertepaars zu finden. Sie können optional ein drittes Argument annehmen, das die Vergleichsfunktion definiert, die anstatt des Kleiner-als-Operators (Operator <) verwendet werden soll. Bei den Argumenten handelt es sich um Werte, nicht um Iteratoren:

template <class T>

const T& max(const T& a, const T& b [, Compare ] );

template <class T>

const T& min(const T& a, const T& b [, Compare ] );

Die Maximal- und Minimalfunktionen werden für ganze Sequenzen durch die allgemeinen Algorithmen max_element() und min_element()generalisiert. Für diese Funktionen sind die Argumente Eingabeoperatoren.

ForwardIterator max_element (ForwardIterator first,

ForwardIterator last [, Compare ] );

ForwardIterator min_element (ForwardIterator first,

ForwardIterator last [, Compare ] );

Hinweis:	Die Maximum- und Minimumalgorithmen können mit allen Datentypen verwendet werden, die die Standardbibliothek zur Verfügung stellt. Bei den geordneten Datentypen set und map können Sie jedoch einfacher auf die Maximal- und Minimalwerte zugreifen, da es sich um das erste und das letzte Element der Struktur handelt.

Diese Algorithmen liefern einen Iterator zurück, der den größten bzw. den kleinsten Wert in einer Sequenz angibt. Genügt mehr als ein Wert der Bedingung, besteht das Rückgabeergebnis aus dem ersten entsprechenden Wert. Beide Algorithmen können optional ein drittes Argument annehmen, bei dem es sich um die Funktion handelt, die als Vergleichsoperator anstatt des Standardoperators verwendet werden soll.

Das Beispielprogramm stellt mehrere Verwendungsmöglichkeiten dieser Algorithmen vor. Die Funktion namens split() im String-Beispiel, die Strings in Wörter zerlegt, wird unter Beispielfunktion: Zerlegen einer Zeile in Wörter beschrieben. Die Funktion randomInteger() wird unter Random Access-iteratoren. beschrieben.

void max_min_example ()

// Verwendung der Algorithmen max_element und min_element

{

// Erzeugen eines Vektors aus Zufallszahlen zwischen 0 und 99

vector<int> numbers(25);

for (int i = 0; i < 25; i++)

numbers[i] = randomInteger(100);

// Ausgeben des Maximalwerts

vector<int>::iterator max =

max_element(numbers.begin(), numbers.end());

cout << "largest value was " << * max << endl;

// Beispiel für die Verwendung von Strings

string text =

"It was the best of times, it was the worst of times.";

list<string> words;

split (text, " .,!:;", words);

cout << "The smallest word is "

<< * min_element(words.begin(), words.end())

<< " and the largest word is "

<< * max_element(words.begin(), words.end())

<< endl;

}


Suchen der ersten nicht übereinstimmenden Elemente in parallelen Sequenzen

Der Name mismatch() könnte zu der Fehlinterpretation führen, daß dieser Algorithmus das Gegenstück zum Algorithmus equal() darstellt, der untersucht, ob zwei Sequenzen gleich sind (vgl. Testen zweier Sequenzen auf paarweise Gleichheit). Der Algorithmus mismatch() gibt jedoch ein Iteratorenpaar zurück, das zusammen die erste Position angibt, an der bei zwei parallelen Sequenzen ein unterschiedliches Element auftritt. (Das Strukturpaar wird unter Datenabstraktion map beschrieben.) Die zweite Sequenz wird nur durch eine Anfangsposition ohne Endposition angegeben. Es wird davon ausgegangen (nicht jedoch überprüft), daß die zweite Sequenz mindestens so viele Elemente wie die erste Sequenz enthält. Die Argumente und der Rückgabetyp von mismatch() können folgendermaßen beschrieben werden:

pair<InputIterator, InputIterator> mismatch

(InputIterator first1, InputIterator last1,

InputIterator first2 [, BinaryPredicate ] );

Die Elemente der beiden Sequenzen werden Element für Element parallel untersucht. Sobald eine Nichtübereinstimmung gefunden wird, also ein Punkt, an dem sich die beiden Sequenzen unterscheiden, wird ein Pair erzeugt, das Iteratoren enthŠlt, die die Positionen der sich unterscheidenden Elemente angibt. Wenn die erste Sequenz durchlaufen wurde, bevor nicht übereinstimmende Elemente entdeckt werden konnten, enthält das Ergebnispaar den Endwert der ersten Sequenz und den letzten untersuchten Wert der zweiten Sequenz. (Die zweite Sequenz muß noch nicht erschöpft sein).

Das Beispielprogramm demonstriert die Verwendung dieses Verfahrens. Die Funktion mismatch_test() nimmt zwei String-Werte als Argumente an. Diese werden lexikographisch verglichen, und es wird eine Meldung über ihre relative Reihenfolge ausgegeben. (Der Vorgang ähnelt der durch den Algorithmus lexicographic_compare() durchgeführten Analyse. Diese Funktion liefert jedoch nur einen booleschen Wert zurück.) Da der Algorithmus mismatch() von der Annahme ausgeht, daß die zweite Sequenz mindestens so lang wie die erste ist, wird zuerst ein Vergleich der beiden String-Längen durchgeführt. Die Argumente werden anschließend vertauscht, wenn der zweite String kürzer als der erste ist. Nach dem Aufruf von mismatch() werden die Elemente des Ergebnispaars in ihre Komponenten zerlegt. Diese Teile werden anschließend getestet, um die korrekte Reihenfolge zu bestimmen.

void mismatch_test (char * a, char * b)

// Verwendung des Algorithmus mismatch

{

pair<char *, char *> differPositions(0, 0);

char * aDiffPosition;

char * bDiffPosition;

if (strlen(a) < strlen(b)) {

// Sicherstellen, daß der längere String an zweiter Stelle steht

differPositions = mismatch(a, a + strlen(a), b);

aDiffPosition = differPositions.first;

bDiffPosition = differPositions.second;

}

else {

differPositions = mismatch(b, b + strlen(b), a);

// Beachten Sie die umgekehrte Reihenfolge

aDiffPosition = differPositions.second;

bDiffPosition = differPositions.first;

}

// Vergleich der sich ergebenden Werte

cout << "string " << a;

if (*aDiffPosition == *bDiffPosition)

cout << " is equal to ";

else if (*aDiffPosition < *bDiffPosition)

cout << " is less than ";

else

cout << " is greater than ";

cout << b << endl;

}

Eine zweite Form des Algorithmus mismatch() ähnelt der beschriebenen Form mit dem Unterschied, daß sie ein binäres Prädikat als viertes Argument annehmen kann. Die Binärfunktion wird zum Vergleichen von Elementen statt des Operators == verwendet.


Stationäre Transformationen

Hinweis:	Die in diesem Abschnitt beschriebenen Beispielfunktionen finden Sie in der Datei alg3.cpp.

Die nächste Kategorie von Algorithmen in der Standardbibliothek, die untersucht werden soll, bezieht sich auf die Algorithmen, mit denen Sequenzen bearbeitet und transformiert werden, ohne sie von ihrem ursprünglichen Speicherort zu verschieben. Einige dieser Routinen, zum Beispiel replace(), können in einer Version mit einer Kopie arbeiten und in der anderen Version mit den Originalalgorithmen für stationäre Transformationen. Sollte es bei den anderen Routinen erforderlich sein, das Original zu erhalten, kann vor dem Durchführen der Transformationen eine Kopie der Sequenz erstellt werden. Das folgende Beispiel zeigt, wie die Umkehrung eines Vektors in einen anderen, neu zugewiesenen Vektor gesetzt werden kann.

vector<int> newVec(aVec.size());

copy (aVec.begin(), aVec.end(), newVec.begin()); 	// erst kopieren

reverse (newVec.begin(), newVec.end());  	// anschließend umkehren

Viele der Algorithmen, die als Operationen zur Sequenzerzeugung beschrieben werden, beispielsweise transform() (Transformieren einer oder zweier Sequenzen) oder partial_sum (Teilsummen), können auch eingesetzt werden, um einen Wert stationär zu bearbeiten, indem einfach der gleiche Iterator sowohl als Eingabe- als auch als Ausgabeangabe verwendet wird.


Umkehren der Elemente in einer Sequenz

Der Algorithmus reverse() kehrt die Elemente in einer Sequenz um. Das letzte Element wird zum ersten, und das erste Element zum neuen letzten Element. Die Argumente werden als bidirektionale Iteratoren angesehen, und es wird kein Wert zurückgeliefert.

void reverse (BidirectionalIterator first,

BidirectionalIterator last);

Das Beispielprogramm zeigt zwei Verwendungsmöglichkeiten für diesen Algorithmus. Das erste Beispiel kehrt einen Array von Zeichenwerten um. Der Algorithmus reverse() kann auch mit Listenwerten verwendet werden, wie das zweite Beispiel demonstriert. Eine Liste wird dort in aufsteigender Reihenfolge mit den Werten 2 bis 11 initialisiert. (Hierzu wird das Funktionsobjekt iotaGen verwendet, zu dem Sie unter Funktionsobjekte) weitere Einzelheiten finden. Die Liste wird anschließend umgekehrt; als Ergebnis enthält die Liste nun die Werte 11 bis 2 in absteigender Reihenfolge. Beachten Sie, daß die Datenstruktur List eine eigene Elementfunktion reverse() zur Verfügung stellt.

void reverse_example ()

// Verwendung des Algorithmus reverse

{

// Beispiel 1, Umkehren eines Strings

char * text = "Rats live on no evil star";

reverse (text, text + strlen(text));

cout << text << endl;

// Beispiel 2, Umkehren einer Liste

list<int> iList;

generate_n (inserter(iList, iList.begin()), 10, iotaGen(2));

reverse (iList.begin(), iList.end());

}


Ersetzen bestimmter Elemente durch einen festen Wert

Die Algorithmen replace() und replace_if() werden dazu verwendet, das Vorkommen bestimmter Elemente durch einen neuen Wert zu ersetzen. Beide Algorithmen verwenden den gleichen neuen Wert unabhängig davon, wie viele Ersetzungsvorgänge durchgeführt werden. Wenn Sie den Algorithmus replace() verwenden, werden alle Vorkommen eines bestimmten Testwerts durch den neuen Wert ersetzt. Wenn Sie mit replace_if() arbeiten, werden alle Elemente, die einer Prädikatfunktion genügen, durch einen neuen Wert ersetzt. Bei den Iterator-Argumenten muß es sich um Vorwärtsiteratoren handeln.

Die Algorithmen replace_copy() und replace_copy_if() sind replace() und replace_if() ähnlich. Die Originalsequenz bleibt jedoch intakt, und die neuen Werte werden in eine neue Sequenz eingesetzt, bei der es sich um einen anderen Typ handeln kann.

void replace (ForwardIterator first, ForwardIterator last,

const T&, const T&);

void replace_if (ForwardIterator first, ForwardIterator last,

Predicate, const T&);

OutputIterator replace_copy (InputIterator, InputIterator,

OutputIterator, const T&, const T&);

OutputIterator replace_copy (InputIterator, InputIterator,

OutputIterator, Predicate, const T&);

Im Beispielprogramm werden einem Vektor anfangs die Werte 0 1 2 3 4 5 4 3 2 1 0 zugewiesen. Ein Aufruf von replace() ersetzt den Wert 3 durch den Wert 7; das Ergebnis ist der Vektor 0 1 2 7 4 5 4 7 2 1 0. Der Aufruf von replace_if() ersetzt alle geraden Zahlen durch den Wert 9; das Ergebnis ist der Vektor 9 1 9 7 9 5 9 7 9 1 9.

void replace_example ()

// Verwendung des Algorithmus replace

{

// Vector 0 1 2 3 4 5 4 3 2 1 0 erzeugen

vector<int> numbers(11);

for (int i = 0; i < 11; i++)

numbers[i] = i < 5 ? i : 10 - i;

// 3 durch 7 ersetzen

replace (numbers.begin(), numbers.end(), 3, 7);

// gerade Zahlen durch 9 ersetzen

replace_if (numbers.begin(), numbers.end(), isEven, 9);

// Kopieversionen von replace

int aList[] = {2, 1, 4, 3, 2, 5};

int bList[6], cList[6], j;

replace_copy (aList, aList+6, &bList[0], 2, 7);

replace_copy_if (bList, bList+6, &cList[0],

bind2nd(greater<int>(), 3), 8);

}

Das Beispielprogramm stellt auch die Verwendung der Algorithmen replace_copy vor. Zuerst wird ein Array mit den Werten 2 1 4 3 2 5 erzeugt. Das Array wird bearbeitet, indem die Werte 2 durch 7 ersetzt werden; das Ergebnis ist das Array 7 1 4 3 7 5. Anschließend werden alle Werte, die größer als 3 sind, durch den Wert 8 ersetzt; als Array-Werte ergeben sich 8 1 8 3 8 8. In diesem Beispiel wird der Funktionsadapter bind2nd() verwendet, um die binäre Größer-als-Funktion zu modifizieren, indem das zweite Argument an den konstanten Wert 3 gebunden wird und so die unäre Funktion x > 3 erzeugt wird.


Rotieren von Elementen um einen Mittelpunkt

Bei der Rotation einer Sequenz wird die Sequenz in zwei Abschnitte geteilt und anschließend die Reihenfolge der Teilabschnitte vertauscht. Die relative Reihenfolge der Elemente innerhalb der beiden Teilabschnitte bleibt erhalten. Angenommen, die Reihenfolge der Werte ist

1 2 3 4 5 6 7 8 9 10

Wenn nun um das Element 7 rotiert wird, werden die Werte 7 bis 10 an den Anfang verschoben und die Elemente 1 bis 6 an das Ende. Als Ergebnis erhält man die folgende Sequenz:

7 8 9 10 1 2 3 4 5 6

Wenn Sie den Algorithmus rotate() aufrufen, werden der Anfangspunkt, der Mittelpunkt und der Endpunkt durch Vorwärtsiteratoren bezeichnet:

void rotate (ForwardIterator first, ForwardIterator middle,

ForwardIterator last);

Der Präfixteil, die Elementmenge, die dem Anfangspunkt folgt und nicht den Mittelpunkt enthält, wird mit dem Suffixteil, der Elementmenge zwischen Mittelpunkt und Endpunkt, vertauscht. Beachten Sie, daß diese beiden Segmente nicht die gleiche Länge besitzen müssen, wie auch das Beispiel zeigt.

void rotate_example()

// Verwendung des Algorithmus rotate

{

// Erzeugen der Liste 1 2 3 ... 10

list<int> iList;

generate_n(inserter(iList, iList.begin()), 10, iotaGen(1));

// Suchen der Position des Elements Sieben

list<int>::iterator & middle =

find(iList.begin(), iList.end(), 7);

// Rotieren um diesen Punkt

rotate (iList.begin(), middle, iList.end());

// Nochmals um diesen Punkt rotieren

list<int> cList;

rotate_copy (iList.begin(), middle, iList.end(),

inserter(cList, cList.begin()));

}

Im Beispielprogramm wird zuerst eine Liste von Integerwerten in der Reihenfolge von 1 bis 10 erzeugt. Dann wird der Algorithmus find() (Suchen eines Elements, das einer Bedingung genügt) verwendet, um die Position des Elements 7 zu suchen. Dieses Element wird zum Mittelpunkt der Rotation.

Eine zweite Form von rotate() kopiert die Elemente in eine neue Sequenz, anstatt die Werte stationär zu rotieren. Das Beispielprogramm demonstriert auch dieses Verfahren, indem nochmals um den Mittelpunkt (der jetzt den Wert 3 enthält) gedreht wird. Das Ergebnis ist die Liste 3 4 5 6 7 8 9 10 1 2. Die Werte in iList bleiben unverändert.


Aufteilen einer Sequenz in zwei Gruppen

Eine Partition entsteht, wenn alle Elemente, die einem Prädikat genügen, an das eine Ende einer Sequenz verschoben werden und alle Elemente, die dem Prädikat nicht genügen, an das andere Ende. Das Aufteilen von Elementen ist ein Grundverfahren in bestimmten Sortieralgorithmen, beispielsweise quicksort.

BidirectionalIterator partition

(BidirectionalIterator, BidirectionalIterator, Predicate);

BidirectionalIterator stable_partition

(BidirectionalIterator, BidirectionalIterator, Predicate);

Die Standardbibliothek unterstützt zwei Partitionsformen. Die erste, die durch den Algorithmus partition() zur Verfügung gestellt wird, garantiert nur, daß die Elemente in zwei Gruppen aufgeteilt werden. Der Ergebniswert ist ein Iterator, der den endgültigen Mittelpunkt zwischen den beiden Gruppen angibt (Ende der ersten Gruppe plus 1).

Hinweis:	Es gibt eine eindeutige stable partition für jede Sequenz; der Algorithmus partition() kann jedoch eine beliebige Anzahl von Werten zurückliefern. Die folgenden Partitionen sind alle legale Partitionen des Beispielproblems:

2 4 6 8 10 1 3 5 7 9

10 8 6 4 2 5 7 9 3 1

2 6 4 8 10 3 5 7 9 1

6 4 2 10 8 5 3 7 9 1

Der ursprüngliche Vektor im Beispielprogramm enthält die Werte 1 bis 10 in aufsteigender Reihenfolge. Die Partition verschiebt die geraden Elemente an den Anfang und die ungeraden Elemente an das Ende. Als Ergebnis enthält der Vektor die Werte 10 2 8 4 6 5 7 3 9 1, und der Mittelpunktiterator zeigt auf das Element 5.

void partition_example ()

// Verwendung des Algorithmus partition

{

// Erzeugen des Vektors 1 2 3 ... 10

vector<int> numbers(10);

generate(numbers.begin(), numbers.end(), iotaGen(1));

// Gerade Werte an den Anfang, ungerade an das Ende

vector<int>::iterator result =

partition(numbers.begin(), numbers.end(), isEven);

cout << "middle location " << result - numbers.begin() << endl;

// Erzeugen einer stable partition

generate (numbers.begin(), numbers.end(), iotaGen(1));

stable_partition (numbers.begin(), numbers.end(), isEven);

}

Die relative Reihenfolge der Elemente innerhalb einer Partition in dem sich ergebenden Vektor stimmt möglicherweise nicht mit der Reihenfolge der Werte im Originalvektor überein. Im Original geht zum Beispiel der Wert 4 dem Element 8 voraus; im Ergebnis kann es jedoch auf das Element 8 folgen. Eine zweite Version von partition namens stable_partition() stellt die Reihenfolge der sich ergebenden Werte sicher. Mit der im Beispiel gewählten Eingabe ergibt sich für die stable partition die Sequenz 2 4 6 8 10 1 3 5 7 9. Der Algorithmus stable_partition() ist etwas langsamer und benötigt mehr Speicher als der Algorithmus partition(). Wenn die Reihenfolge der Werte nicht wichtig ist, sollten Sie daher partition() verwenden.


Erzeugen einer Permutationenfolge

Eine Permutation ist eine Neuanordnung von Werten. Wenn Werte miteinander verglichen werden können (dies ist beispielsweise bei Integerwerten, Zeichen oder Wörtern der Fall), ist es möglich, systematisch alle Permutationen einer Sequenz zu erzeugen. Zwei Werte besitzen zum Beispiel zwei Permutationen, drei Werte sechs Permutationen, und vier Werte  24 Permutationen.

Hinweis:	Permutationen können geordnet sein: die niedrigste Permutation enthält die Werte vom kleinsten bis zum größten Wert, und die höchste Permutation ist die Sequenz, die die Werte vom größten bis zum kleinsten Wert aufführt. Betrachten Sie zum Beispiel die Permutationen der Integerwerte 1 2 3. Die sechs Permutationen dieser Werte weisen die folgende Reihenfolge auf:

1 2 3

1 3 2

2 1 3

2 3 1

3 1 2

3 2 1

Beachten Sie, daß die Werte der ersten Permutation alle größer werden, während sie in der letzten Permutation kleiner werden.

Die Permutations-Algorithmen sind folgendermaßen definiert:

bool next_permutation (BidirectionalIterator first,

BidirectionalIterator last, [ Compare ] );

bool prev_permutation (BidirectionalIterator first,

BidirectionalIterator last, [ Compare ] );

Das zweite Beispiel im Beispielprogramm zeigt das gleiche Verfahren. Es verwendet nur Zeiger auf Zeichen-Arrays anstelle von Integerwerten. In diesem Fall muß eine andere Vergleichsfunktion zu Verfügung gestellt werden, da der Standardoperator nur Zeigeradressen vergleichen würde.

bool nameCompare (char * a, char * b) { return strcmp(a, b) <= 0; }

void permutation_example ()

// Verwendung des Algorithmus next_permutation

{

// Beispiel 1, Permutation der Werte 1 2 3

int start [] = { 1, 2, 3};

do

copy (start, start + 3,

ostream_iterator<int> (cout, " ")), cout << endl;

while (next_permutation(start, start + 3));

// Beispiel 2, Permutation von Wörtern

char * words = {"Alpha", "Beta", "Gamma"};

do

copy (words, words + 3,

ostream_iterator<char *> (cout, " ")), cout << endl;

while (next_permutation(words, words + 3, nameCompare));

// Beispiel 3, Umkehrungs-Permutation von Zeichen

char * word = "bela";

do

cout << word << ' ';

while (prev_permutation (word, &word[4]));

cout << endl;

}

Beispiel 3 im Beispielprogramm demonstriert die Verwendung des Algorithmus zur Umkehrungs-Permutation, der Werte in umgekehrter Reihenfolge erzeugt. Dieses Beispiel beginnt mitten in einer Sequenz und nicht am Anfang. Die restlichen Permutationen des Worts "bela" sind beal, bale, bael, aleb, albe, aelb, aebl, able und abel.


Zusammenführen zweier benachbarter Sequenzen in einer Sequenz

Die Operation merge erfordert zwei geordnete Sequenzen, die in einer einzigen geordneten Sequenz zusammengeführt werden. Dabei werden die Elemente aus jeder Kollektion so überlappend angeordnet, wie es zur Erzeugung der neuen Liste erforderlich ist. Der Algorithmus inplace_merge() geht davon aus, daß eine Sequenz in zwei benachbarte Abschnitte, von denen jeder geordnet ist, geteilt wird. Das Zusammenführen kombiniert die beiden Abschnitte zu einem Abschnitt und verschiebt dabei bei Bedarf Elemente. (Der alternative Algorithmus merge(), der an anderer Stelle beschrieben wird, kann zum Zusammenführen zweier einzelner Sequenzen in eine Sequenz verwendet werden.) Die Argumente von inplace_merge() müssen bidirektionale Iteratoren sein.

void inplace_merge (BidirectionalIterator first,

BidirectionalIterator middle,

BidirectionalIterator last [, BinaryFunction ] );

Das Beispielprogramm demonstriert die Verwendung des Algorithmus inplace_merge() mit einem Vektor und einer Liste. Die Sequenz 0 2 4 6 8 1 3 5 7 9 wird in einen Vektor geschrieben. Ein Aufruf von find() (Suchen eines Elements, das einer Bedingung genügt) wird verwendet, um den Anfang der Folge ungerader Zahlen zu suchen. Die zwei Aufrufe von inplace_merge() kombinieren anschließend die beiden Sequenzen in einer Sequenz.

void inplace_merge_example ()

// Verwendung des Algorithmus inplace_merge

{

// Erzeugen der Sequenz 0 2 4 6 8 1 3 5 7 9

vector<int> numbers(10);

for (int i = 0; i < 10; i++)

numbers[i] = i < 5 ? 2 * i : 2 * (i - 5) + 1;

// Suchen des Mittelpunkts

vector<int>::iterator midvec =

find(numbers.begin(), numbers.end(), 1);

// Kopieren der Zahlen in eine Liste

list<int> numList;

copy (numbers.begin(), numbers.end(),

inserter (numList, numList.begin()));

list<int>::iterator midList =

find(numList.begin(), numList.end, 1);

// Zusammenführen der Listen

inplace_merge (numbers.begin(), midvec, numbers.end());

inplace_merge (numList.begin(), midList, numList.end());

}


Zufallsanordnung der Elemente einer Sequenz

Der Algorithmus random_shuffle() ordnet die Elemente in einer Sequenz nach dem Zufallsprinzip neu an. Genau n Vertauschungen werden durchgeführt, wobei n für die Anzahl der Elemente in der Sequenz steht. Das Ergebnis ist natürlich nicht vorhersagbar. Da es sich bei den Argumenten um Random Access-Iteratoren handeln muß, kann dieser Algorithmus nur für Vektoren, Deques oder gewöhnliche Zeiger verwendet werden. Er kann nicht für Listen, Sets oder Maps verwendet werden.

void random_shuffle (RandomAccessIterator first,

RandomAccessIterator last [, Generator ] );

Die alternative Version des Algorithmus verwendet ein optionales drittes Element. Dieser Wert muß ein Zufallszahlengenerator sein. Dieser Generator muß als Argument einen positiven Wert m annehmen und einen Wert zwischen 0 und m-1 zurückliefern. Ebenso wie beim Algorithmus generate() kann diese Zufallszahlenfunktion ein beliebiger Objekttyp sein, der auf den Funktionsaufrufoperator reagiert.

void random_shuffle_example ()

// Verwendung des Algorithmus random_shuffle

{

// Erzeugen des Vektors mit dem Inhalt 1 2 3 ... 10

vector<int> numbers;

generate(numbers.begin(), numbers.end(), iotaGen(1));

// Elemente zufällig neu anordnen

random_shuffle (numbers.begin(), numbers.end());

// Mit explizitem Zufallszahlengenerator wiederholen

struct RandomInteger {

{

operator() (int m) { return rand() % m; }

} random;

random_shuffle (numbers.begin(), numbers.end(), random);

}


Entfernungsalgorithmen

Hinweis:	Die in diesem Abschnitt beschriebenen Algorithmen richten eine Sequenz so ein, daß die gewünschten Elemente an den Anfang verschoben werden. Die restlichen Werte werden nicht tatsächlich entfernt. Es wird nur die Anfangsposition für diese Werte zurückgegeben. Die Werte können anschließend durch einen Aufruf von erase() gelöscht werden. Denken Sie daran, daß die Entfernungsalgorithmen die unerwünschten Elemente nicht tatsächlich entfernen.

Die folgenden zwei Algorithmen wirken anfangs vielleicht etwas verwirrend. Beide behaupten, bestimmte Werte aus einer Sequenz zu entfernen. Tatsächlich wird die Größe der Sequenz jedoch von keinem der beiden Algorithmen verändert. Beide funktionieren so, daß die Werte, die erhalten werden sollen, an den Anfang der Sequenz verschoben werden. Sie geben einen Iterator zurück, der das Ende der Sequenz angibt. Die Elemente hinter diesem Iterator sind einfach die Originalwerte der Sequenz, die nicht verändert wurden. Dieses Verfahren ist deshalb erforderlich, weil der generische Algorithmus den Container nicht kennt, den er bearbeitet. Er besitzt nur einen generischen Iterator. Dies ist einer der Nachteile, die generische Algorithmen mit sich bringen. In den meisten Fällen wird der Benutzer das Iteratorergebnis als Argument der Elementfunktion erase() für den Container verwenden wollen und so die Werte vom Iterator bis zum Ende der Sequenz löschen.

Das folgende einfache Beispiel zeigt dieses Verfahren. Angenommen, Sie möchten die geraden Zahlen aus der Sequenz 1 2 3 4 5 6 7 8 9 10 entfernen. Sie können dazu den Algorithmus remove_if() verwenden. Der Algorithmus remove_if() führt zu folgender Sequenz:

1 3 5 7 9 | 6 7 8 9 10

Die vertikale Trennlinie gibt die Position des Iterators an, die der Algorithmus remove_if() zurückgibt. Beachten Sie, daß die fünf Elemente vor der Trennlinie das gewünschte Ergebnis darstellen. Die fünf Werte hinter der Linie sind einfach der Originalinhalt dieser Positionen. Wenn Sie diesen Iterator-Wert zusammen mit dem Sequenzendeiterator als Argument für erase() verwenden, können Sie die überflüssigen Werte löschen und das gewünschte Ergebnis erhalten.

Beide hier beschriebenen Algorithmen können in einer alternativen Version, die mit Kopien arbeitet, verwendet werden. Diese Version der Algorithmen verändert das Original nicht und setzt die gewünschten Elemente, die erhalten werden sollen, in eine Ausgabesequenz.

Hinweis:	Die in diesem Abschnitt beschriebenen Beispielfunktionen finden Sie in der Datei alg4.cpp.


Entfernen unerwünschter Elemente

Der Algorithmus remove() eliminiert unerwünschte Werte aus einer Sequenz. Ebenso wie beim Algorithmus find() kann es sich um Werte handeln, die entweder einer bestimmten Konstante entsprechen oder einem gegebenen Prädikat genügen. Die Definition der Argumenttypen ist folgendermaßen:

ForwardIterator remove

(ForwardIterator first, ForwardIterator last, const T &);

ForwardIterator remove_if

(ForwardIterator first, ForwardIterator last, Predicate);

Der Algorithmus remove() kopiert Werte an den Anfang der Sequenz und überschreibt dabei die Position der entfernten Argumente. Alle nicht entfernten Elemente behalten ihre relative Reihenfolge. Nachdem alle Werte untersucht wurden, wird der verbleibende Teil der Sequenz nicht verändert. Der als Ergebnis der Operation zurückgelieferte Iterator gibt das Ende der neuen Sequenz an. Das Eliminieren von Element 2 aus der Sequenz 1 2 4 3 2 führt zu der Sequenz 1 4 3 3 2, und der als Ergebnis zurückgegebene Iterator zeigt auf die zweite 3. Dieser Wert kann als Argument von erase() verwendet werden, um die verbleibenden Elemente zu eliminieren (die 3 und die 2), wie das Beispielprogramm zeigt.

Die Version der Algorithmen, die mit Kopien arbeitet, kopiert Werte in eine Ausgabesequenz anstatt die Transformationen in der Originalsequenz vorzunehmen.

OutputIterator remove_copy

(InputIterator first, InputIterator last,

OutputIterator result, const T &);

OutputIterator remove_copy_if

(InputIterator first, InputIterator last,

OutputIterator result, Predicate);

Das folgende Programm zeigt die Verwendung von remove().

void remove_example ()

// Verwendung des Algorithmus remove

{

// Erzeugen einer Zahlenliste

int data[] = {1, 2, 4, 3, 1, 4, 2};

list<int> aList;

copy (data, data+7, inserter(aList, aList.begin()));

// Entfernen der Zweien, Kopieren in eine neue Liste

list<int> newList;

remove_copy (aList.begin(), aList.end(),

back_inserter(newList), 2);

// Entfernen der Zweien in der Originalsequenz

list<int>::iterator where;

where = remove (aList.begin(), aList.end(), 2);

aList.erase(where, aList.end());

// Entfernen aller geraden Werte

where = remove_if (aList.begin(), aList.end(), isEven);

aList.erase(where, aList.end());

}


Entfernen von Gruppen gleicher Werte

Der Algorithmus unique() durchsucht eine lineare Sequenz und entfernt alle Elemente außer dem ersten Element aus jeder aufeinanderfolgenden Gruppe gleicher Elemente. Die Argumentfolge wird durch Vorwärtsiteratoren angegeben.

ForwardIterator unique (ForwardIterator first,

ForwardIterator last [, BinaryPredicate ] );

Während der Algorithmus die Kollektion abarbeitet, werden Elemente an den Anfang der Sequenz verschoben und überschreiben die vorhandenen Elemente. Sobald alle eindeutigen Werte identifiziert wurden, wird die verbleibende Sequenz nicht mehr verändert. Eine Sequenz wie zum Beispiel 1 3 3 2 2 2 4 wird zu 1 3 2 4 | 2 2 4. Die vertikale Trennlinie gibt die Position an, die der Ergebniswert des Iterators zurückliefert. Diese Position gibt das Ende der eindeutigen Sequenz und den Anfang der übrig gebliebenen Elemente an. Bei den meisten Containern kann der Rückgabewert des Algorithmus als Argument eines anschließenden Aufrufs von erase() verwendet werden, um die unerwünschten Elemente aus der Kollektion zu entfernen. Das Beispielprogramm zeigt dieses Verfahren.

Eine Version des Algorithmus, die mit Kopien  arbeitet, kopiert die eindeutigen Werte in einen Ausgabeiterator anstatt die Veränderungen in der Originalsequenz vorzunehmen. Bei der Transformation einer Liste oder eines Multisets kann ein Einfügeiterator verwendet werden, um die Kopieroperationen des Ausgabeiterators in Einfügeoperationen zu ändern.

OutputIterator unique_copy

(InputIterator first, InputIterator last,

OutputIterator result [, BinaryPredicate ] );

Das Beispielprogramm zeigt dies:

void unique_example ()

// Verwendung des Algorithmus unique

{

// Erzeugen einer Liste von Werten

int data[] = {1, 3, 3, 2, 2, 4};

list<int> aList;

set<int> aSet;

copy (data, , inserter(aList, aList.begin()));

// Kopieren eindeutiger Elemente in eine Menge (Set)

unique_copy (aList.begin(), aList.end(),

inserter(aSet, aSet.begin()));

// Kopieren eindeutiger Elemente in der Originalsequenz

list<int>::iterator where;

where = unique(aList.begin(), aList.end());

// Entfernen der überflüssigen Werte

aList.erase(where, aList.end());

}


Algorithmen, die zu einem skalaren Ergebnis führen

Hinweis:	Die in diesem Abschnitt beschriebenen Beispielfunktionen finden Sie in der Datei alg5.cpp.

Die nächste Kategorie von Algorithmen reduziert eine ganze Sequenz auf einen einzelnen skalaren Wert.

Beachten Sie, daß zwei dieser Algorithmen, accumulate() und inner_product() in der Header-Datei numeric definiert sind und nicht wie alle anderen generischen Algorithmen in der Header-Datei algorithm.


Zählen der Elemente, die einer Bedingung genügen

Die Algorithmen count() und count_if() werden verwendet, um die Anzahl der Elemente zu bestimmen, die einem gegebenen Wert entsprechen bzw. einem gegebenen Prädikat genügen. Beide nehmen als Argument eine Referenz auf einen Zählwert (normalerweise einen Integerwert) an und inkrementieren diesen Wert. Beachten Sie, daß der Zählwert als Argument durch Referenz übergeben und nicht als Wert der Funktion zurückgeliefert wird. Die Funktion count() selbst gibt keinen Wert zurück.

void count (InputIterator first, InputIterator last,

const T&, Size &);

void count_if (InputIterator first, InputIterator last,

Predicate, Size &);

Hinweis:	Beachten Sie, daß die Algorithmen count() die Summe nicht als Funktionsergebnis zurückgeben, sondern einfach dem letzten Argument in ihrer Parameterliste hinzufügen, das durch Referenz übergeben wird. Dies bedeutet, daß aufeinanderfolgende Aufrufe dieser Funktionen verwendet werden können, um eine Gesamtsumme zu erzeugen. Es bedeutet aber auch, daß Sie die dieser letzten Argumentposition übergebene Variable initialisieren müssen, bevor Sie einen dieser Algorithmen aufrufen.

Der Quelltextausschnitt demonstriert die Verwendung dieser Algorithmen. Durch den Aufruf von count() wird das Vorkommen des Buchstabens e im Beispiel-String gezählt. Durch den Aufruf von count_if() werden die Vokale gezählt.

void count_example ()

// Verwendung des Algorithmus count

{

int eCount = 0;

int vowelCount = 0;

char * text = "Now is the time to begin";

count (text, text + strlen(text), 'e', eCount);

count_if (text, text + strlen(text), isVowel, vowelCount);

cout << "There are " << eCount << " letter e's " << endl

<< "and " << vowelCount << " vowels in the text:"

<< text << endl;

}


Reduzieren der Sequenz auf einen Einzelwert

Das Ergebnis, das der Algorithmus accumulate() erzeugt, ist der Wert, der sich ergibt, wenn zwischen jedes Element der Sequenz ein binärer Operator gesetzt und das Ergebnis ausgewertet wird. Der Operator ist auf den Additionsoperator , +, voreingestellt. Diese Voreinstellung kann jedoch durch eine beliebige binäre Funktion ersetzt werden. Ein Initialwert (eine Identität) muß zur Verfügung gestellt werden. Dieser Wert wird bei leeren Sequenzen zurückgeliefert und ansonsten als linkes Argument in der ersten Berechnung verwendet.

ContainerType accumulate (InputIterator first, InputIterator last,

ContainerType initial [, BinaryFunction ] );

Das Beispielprogramm demonstriert die Verwendung von accumulate(), um die Summe und das Produkt eines Vektors aus Integerwerten zu erhalten. Im ersten Fall ist die Identität Null, und der Standardoperator + wird verwendet. Im zweiten Aufruf ist die Identität 1, und der Multiplikationsoperator (namens times) wird explizit als viertes Argument übergeben.

void accumulate_example ()

// Verwendung des Algorithmus accumulate

{

int numbers[] = {1, 2, 3, 4, 5};

// Erstes Beispiel: einfache Akkumulation

int sum = accumulate (numbers, numbers + 5, 0);

int product =

accumulate (numbers, numbers + 5, 1, times<int>());

cout << "The sum of the first five integers is " << sum << endl;

cout << "The product is " << product << endl;

// Zweites Beispiel: andere Typen als Initialwerte

list<int> nums;

nums = accumulate (numbers, numbers+5, nums, intReplicate);

}

list<int>& intReplicate (list<int>& nums, int n)

// Hinzufügen der Folge n bis 1 zum Ende der Liste

{

while (n) nums.push_back(n--);

return nums;

}

Weder der Identitätswert noch das Ergebnis der binären Funktion müssen dem Containertyp entsprechen. Das Beispielprogramm zeigt dies im zweiten Beispiel durch den Aufruf von accumulate(). Die Identität ist dort eine leere Liste. Die Funktion nimmt als Argument eine Liste und einen Integerwert an (im Quelltext nach dem Beispielprogramm) und fügt wiederholt Werte in die Liste ein. Die eingefügten Werte stellen eine absteigende Sequenz vom Argument bis 1 dar. Für die Beispieleingabe (gleicher Vektor wie im ersten Beispiel) enthält die erzeugte Liste die 15 Werte 1 2 1 3 2 1 4 3 2 1 5 4 3 2 1.


Generalisiertes inneres Produkt

Angenommen, zwei Sequenzen bestehen jeweils aus n Elementen; a1, a2, ... an und b1, b2, ... bn. Das innere Produkt der Sequenzen ist die Summe der parallelen Produkte, also der Wert von a1 * b1 + a2 * b2 + ... + an * bn. Innere Produkte finden sich in vielen wissenschaftlichen Berechnungen. Beispielsweise ist das innere Produkt einer Zeile multipliziert mit einer Spalte das Kernstück des traditionellem Matrixmultiplikationalgorithmus. Ein generalisiertes inneres Produkt verwendet die gleiche Struktur; die Additions- und Multipikationsoperatoren können jedoch durch arbiträre binäre Funktionen ersetzt werden. Die Standardbibliothek enthält den folgenden Algorithmus zur Berechnung eines inneren Produkts:

ContainerType inner_product

(InputIterator first1, InputIterator last1,

InputIterator first2, ContainerType initialValue

[ , BinaryFunction add, BinaryFunction times ] );

Die ersten drei Argumente des Algorithmus inner_product() definieren die beiden Eingabesequenzen. Die zweite Sequenz wird nur durch den Anfangsiterator angegeben, und es wird angenommen, daß sie mindestens die gleiche Anzahl Elemente wie die erste Sequenz besitzt. Das nächste Argument ist ein Initialwert (Identität), der für den Summierungsoperator verwendet wird. Die Identität, die im Algorithmus accumulate() verwendet wird, ist ähnlich. In der Funktion generalisiertes inneres Produkt sind die beiden letzten Argumente die binären Funktionen, die anstelle des Additionsoperators bzw. des Multiplikationsoperators verwendet werden.

Im Beispielprogramm demonstriert der zweite Aufruf die Verwendung alternativer Funktionen als Argumente. Die Multiplikation wird durch einen Gleichheitstest ersetzt, und die Addition wird durch ein logisches oder ersetzt. Das Ergebnis ist true, wenn mindestens eines der Paare gleich ist und false, wenn dies nicht der Fall ist. Wenn Sie anstelle des oder ein und verwenden, ist der Test nur true, wenn alle Paare gleich sind. Der im nächsten Abschnitt behandelte Algorithmus equal() führt zum gleichen Ergebnis.

void inner_product_example ()

// Verwendung des Algorithmus inner_product

{

int a[] = {4, 3, -2};

int b[] = {7, 3, 2};

// Beispiel 1, ein einfaches inneres Produkt

int in1 = inner_product(a, a+3, b, 0);

cout << "Inner product is " << in1 << endl;

// Beispiel 2, benutzerdefinierte Operationen

bool anyequal = inner_product(a, a+3, b, true,

logical_or<bool>(), equal_to<int>());

cout << "any equal? " << anyequal << endl;

}


Testen zweier Sequenzen auf paarweise Gleichheit

Der Algorithmus equal() testet zwei Sequenzen auf paarweise Gleichheit. Durch die Verwendung eines alternativen binären Prädikats kann er auch für viele andere paarweise Tests paralleler Sequenzen verwendet werden. Die Argumente sind einfache Eingabeiteratoren:

bool equal (InputIterator first, InputIterator last,

InputIterator first2 [, BinaryPredicate] );

Hinweis:	Indem man das binäre Prädikat durch eine andere Funktion ersetzt, ergeben sich verschiedenen Verwendungsmöglichkeiten für die Algorithmen equal und mismatch. Verwenden Sie den Algorithmus equal(), wenn der paarweise Test ein boolesches Ergebnis zurückliefern soll. Verwenden Sie den Algorithmus mismatch(), wenn Sie die Position der Elemente erfahren möchten, die den Test nicht bestehen.

Der Algorithmus equal() geht davon aus (überprüft dies aber nicht), daß die zweite Sequenz mindestens so viele Elemente enthält wie die erste Sequenz. Das Ergebnis true wird zurückgegeben, wenn alle Werte ihrem entsprechenden Element gleich sind. Die alternative Version des Algorithmus ersetzt den Gleichheitstest mit einer arbiträren boolesche Funktion und gibt true zurück, wenn alle paarweisen Elemente dem Prädikat genügen. Im Beispielprogramm wird dies durch Ersetzen des Prädikats durch die Funktion greater_equal() demonstriert. Hierdurch wird nur dann true zurückgegeben, wenn alle Werte der ersten Sequenz größer oder gleich ihrem entsprechenden Wert in der zweiten Sequenz sind.

void equal_example ()

// Verwendung des Algorithmus equal

{

int a[] = {4, 5, 3};

int b[] = {4, 3, 3};

int c[] = {4, 5, 3};

cout << "a = b is: " << equal(a, a+3, b) << endl;

cout << "a = c is: " << equal(a, a+3, c) << endl;

cout << "a pair-wise greater-equal b is: "

<< equal(a, a+3, b, greater_equal<int>()) << endl;

}


Lexikalischer Vergleich

Ein lexikalischer Vergleich zweier Sequenzen läßt sich anhand der Charakteristika seiner häufigsten Verwendungsform erläutern, nämlich dem Vergleich zweier Wörter mit dem Zweck, sie in alphabetischer Reihenfolge (Lexikonreihenfolge) zu ordnen. Beim Vergleich zweier Wörter werden die Elemente (also die Buchstaben) der beiden Sequenzen paarweise verglichen. Solange sie übereinstimmen, schreitet der Algorithmus zum nächsten Buchstaben fort. Wenn sich zwei Zeichenpaare nicht entsprechen, bestimmt das im Alphabet frühere Zeichen das frühere Wort. Daher steht zum Beispiel everybody an früherer Stelle als everything, da das b des ersten Worts im Alphabet früher vorkommt als das t. Sollte eine der beiden Sequenzen vor der anderen beendet sein, wird angenommen, daß die beendete Sequenz die alphabetisch frühere ist. Daher steht zum Beispiel every sowohl vor everybody als auch vor everything, jedoch hinter eve. Wenn beide Sequenzen gleichzeitig beendet sind und sich in allen Fällen paarweise Zeichen entsprechen, werden die beiden Wörter als gleich betrachtet.

Der Algorithmus lexicographical_compare() implementiert dieses Verfahren. Er gibt true zurück, wenn die erste Sequenz kleiner als die zweite ist und false, wenn dies nicht der Fall ist. Der Algorithmus wurde für beliebige Sequenzen verallgemeinert. Daher kann der Algorithmus lexicographical_compare() mit Arrays, Strings, Vektoren, Listen und allen anderen Datenstrukturen der Standardbibliothek verwendet werden..

bool lexicographical_compare

(InputIterator first1, InputIterator last1,

InputIterator first2, InputIterator last2 [, BinaryFunction ] );

Anders als die meisten anderen Algorithmen, die zwei Sequenzen als Argument annehmen, verwendet der Algorithmus lexicographical_compare() den ersten Iterator und den Bereichsendeiterator für beide Sequenzen. Eine Variante des Algorithmus nimmt außerdem ein fünftes Argument an. Dabei handelt es sich um die binäre Funktion, die zum Vergleichen entsprechender Elemente aus den zwei Sequenzen verwendet wird.

Das Beispielprogramm demonstriert die Verwendung dieses Algorithmus mit Zeichensequenzen und mit Integerwert-Arrays.

void lexicographical_compare_example()

// Verwendung des Algorithmus lexicographical_compare

{

char * wordOne = "everything";

char * wordTwo = "everybody";

cout << "compare everybody to everything " <<

lexicographical_compare(wordTwo, wordTwo + strlen(wordTwo),

wordOne, wordOne + strlen(wordOne)) << endl;

int a[] = {3, 4, 5, 2};

int b[] = {3, 4, 5};

int c[] = {3, 5};

cout << "compare a to b:" <<

lexicographical_compare(a, a+4, b, b+3) << endl;

cout << "compare a to c:" <<

lexicographical_compare(a, a+4, c, c+2) << endl;

}


Sequenzerzeugende Algorithmen

Hinweis:	Die in diesem Abschnitt beschriebenen Beispielfunktionen finden Sie in der Datei alg6.cpp.

Die in diesem Abschnitt beschriebenen Algorithmen dienen alle dazu, aus einer vorhandenen Sequenz eine neue Sequenz zu erzeugen, indem eine Transformation durchgeführt wird. Meistens wird die Ausgabesequenz durch einen Ausgabeiterator beschrieben. Das bedeutet, daß diese Algorithmen dazu verwendet werden können, eine vorhandene Struktur zu überschreiben (beispielsweise einen Vektor). Alternativ kann ein Einfügeiterator  (siehe auch Einfügeiteratoren) verwendet werden, damit diese Algorithmen die neuen Elemente in eine Struktur variabler Länge, beispielsweise in einem Set oder eine Liste, einfügen können. Außerdem kann der Ausgabeiterator gleich einer der Sequenzen sein, die durch einen Eingabeiterator angegeben wurde. Hierdurch wird es möglich, eine stationäre Transformation vorzunehmen. Sie finden im folgenden Hinweise, wann dies möglich ist.

Die Funktionen partial_sum() und adjacent_difference() werden in der Header-Datei numeric beschrieben; die anderen Funktionen werden in der Header-Datei algorithm beschrieben.


Umwandeln von einer oder zwei Sequenzen

Der Algorithmus transform() wird entweder verwendet, um eine allgemeine Transformation einer einzelnen Sequenz durchzuführen, oder, um eine neue Sequenz zu erzeugen, indem eine binäre Funktion paarweise auf entsprechende Elemente aus zwei verschiedenen Sequenzen angewendet wird. Die allgemeine Argument- und Ergebnistypdefinition ist folgendermaßen:

OutputIterator transform (InputIterator first, InputIterator last,

OutputIterator result, UnaryFunction);

OutputIterator transform

(InputIterator first1, InputIterator last1,

InputIterator first2,  OutputIterator result, BinaryFunction);

Die erste Form verwendet eine unäre Funktion für jedes Element einer Sequenz. Im Beispielprogramm unten wird dieses Verfahren verwendet, um einen Vektor aus Integerwerten zu erzeugen, die die arithmetische Negation der Werte einer verknüpften Liste enthalten. Die Eingabe- und Ausgabeiteratoren können gleich sein. In diesem Fall wird, wie im Beispielprogramm gezeigt, eine stationäre Transformation vorgenommen.

Die zweite Form arbeitet mit zwei Sequenzen, auf deren entsprechende Elemente die binäre Funktion paarweise angewendet wird. Die Transaktion geht davon aus (überprüft dies aber nicht), daß die zweite Sequenz mindestens so viele Elemente wie die erste Sequenz enthält. Auch hier kann das Ergebnis entweder eine dritte Sequenz oder eine der beiden Eingabesequenzen sein.

int square(int n) { return n * n; }

void transform_example ()

// Verwendung des Algorithmus transform

{

// Erzeugen einer Liste mit den Werten 1 bis 6

list<int> aList;

generate_n (inserter(aList, aList.begin()), 6, iotaGen(1));

// Umwandeln der Elemente in zweite Potenz, Kopieren in Vektor

vector<int> aVec(6);

transform (aList.begin(), aList.end(), aVec.begin(), square);

// Nochmaliges, stationäres Umwandeln des Vektors in vierte Potenz

transform (aVec.begin(), aVec.end(), aVec.begin(), square);

// Paralleles Umwandeln in dritte Potenz

vector<int> cubes(6);

transform (aVec.begin(), aVec.end(), aList.begin(),

cubes.begin(), divides<int>());

}


Teilsummen

Eine Teilsumme  einer Sequenz ist eine neue Sequenz, in der jedes Element durch Hinzufügen der Werte aller vorherigen Werte gebildet wird. Die Teilsumme des Vektors 1 3 2 4 5 ist beispielsweise der neue Vektor 1 4 6 10 15. Das Element 4 wird aus der Summe von 1 + 3 gebildet, das Element 6 aus der Summe von 1 + 3 + 2, usw. Obwohl der Begriff "Summe" zur Beschreibung dieser Operation verwendet wird, kann die binäre Funktion dennoch eine arbiträre Funktion sein. Das Beispielprogramm demonstriert dies durch die Berechnung von Teilprodukten. Die Argumente der Teilsummenfunktion werden folgendermaßen beschrieben:

OutputIterator partial_sum

(InputIterator first, InputIterator last,

OutputIterator result [, BinaryFunction] );

Indem der gleiche Wert für Eingabeiterator und Ergebnis verwendet wird, kann die Teilsumme als stationäre Transformation ermittelt werden.

void partial_sum_example ()

// Verwendung des Algorithmus partial sum

{

// Erzeugen der Werte von 1 bis 5

vector<int> aVec(5);

generate (aVec.begin(), aVec.end(), iotaGen(1));

// Ausgabe Teilsummen

partial_sum (aVec.begin(), aVec.end(),

ostream_iterator<int> (cout, " ")), cout << endl;

// Ausgabe Teilprodukte

partial_sum (aVec.begin(), aVec.end(),

ostream_iterator<int> (cout, " "),

times<int>() );

}


Benachbarte Differenzen

Eine benachbarte Differenz einer Sequenz ist eine neue Sequenz, die durch Ersetzen jedes Elements durch die Differenz zwischen dem jeweiligen Element und dem direkt vorausgehenden Element gebildet wird. Der erste Wert in der neuen Sequenz bleibt unverändert. Eine Sequenz wie zum Beispiel (1, 3, 2, 4, 5) wird in (1, 3-1, 2-3, 4-2, 5-4) umgewandelt und wird so zur Sequenz (1, 2, -1, 2, 1).

Ebenso wie beim Algorithmus partial_sum() ist die Bezeichnung "Differenz" nicht unbedingt zutreffend, da eine arbiträre binäre Funktion verwendet werden kann. Die benachbarten Summen dieser Sequenz sind beispielsweise (1, 4, 5, 6, 9). Die Argumente des Algorithmus besitzen die folgenden Definitionen:

OutputIterator adjacent_difference (InputIterator first,

InputIterator last, OutputIterator result [, BinaryFunction ]);

Indem der gleiche Iterator als Eingabe- und als Ausgabeiterator verwendet wird, können benachbarte Differenzen auch stationär ermittelt werden.

void adjacent_difference_example ()

// Verwendung des Algorithmus adjacent difference

{

// Erzeugen der Werte von 1 bis 5

vector<int> aVec(5);

generate (aVec.begin(), aVec.end(), iotaGen(1));

// Ausgabe benachbarter Differenzen

adjacent_difference (aVec.begin(), aVec.end(),

ostream_iterator<int> (cout, " ")), cout << endl;

// Ausgabe benachbarter Summen

adjacent_difference (aVec.begin(), aVec.end(),

ostream_iterator<int> (cout, " "),

plus<int>() );

}


Verschiedene Algorithmen

In diesem letzten Abschnitt werden die restlichen Algorithmen beschrieben, die die Standardbibliothek enthält.


Anwenden einer Funktion auf alle Elemente in einer Kollektion

Der Algorithmus for_each() nimmt drei Argumente an. Die ersten beiden liefern die Iteratoren, die die auszuwertende Sequenz beschreiben. Das dritte Argument ist eine Ein-Argument-Funktion. Der Algorithmus for_each() wendet die Funktion auf jeden Wert der Sequenz an und übergibt dabei den Wert als Argument.

Function for_each

(InputIterator first, InputIterator last, Function);

Das folgende Quelltextfragment, das die Funktion print_if_leap() verwendet, druckt beispielsweise eine Liste der Schaltjahre, die zwischen 1900 und 1997 auftreten:

cout << "leap years between 1990 and 1997 are: ";

for_each (1990, 1997, print_if_leap);

cout << endl;

Hinweis:	Die Funktion, die als drittes Argument übergeben wird, darf die Sequenz in keiner Weise verändern. Die Funktion kann also nur Ergebnisse durch Nebeneffekte erzielen, beispielsweise durch Drucken, Zuweisen eines Werts zu einer globalen oder statischen Variable oder durch Aufrufen einer weiteren Funktion, die einen Nebeneffekt bewirkt. Wenn die Argumentfunktion ein Ergebnis zurückliefert, wird es ignoriert.

Die Argumentfunktion wird mit Sicherheit nur einmal für jedes Element der Sequenz aufgerufen. Der Algorithmus for_each() selbst gibt den Wert des dritten Arguments zurück. Auch dieses wird jedoch normalerweise ignoriert.

Das folgende Beispiel durchsucht ein Array aus Integerwerten, die Daten darstellen, um herauszufinden, welche Spitzenweinjahrgänge auch Schaltjahre waren:

int vintageYears[] = {1947, 1955, 1960, 1967, 1994};

...

cout << "vintage years which were also leap years are: ";

for_each (vintageYears, vintageYears + 5, print_if_leap);

cout << endl;

Nebeneffekte müssen sich nicht auf Drucken beschränken. Angenommen, Sie arbeiten mit einer Funktion countCaps(), die das Vorkommen von Großbuchstaben zählt:

int capCount = 0;

void countCaps(char c)  { if (isupper(c)) capCount++; }

Das folgende Beispiel zählt die Großbuchstaben in einem String-Wert:

string advice = "Never Trust Anybody Over 30!";

for_each(advice.begin(), advice.end(),countCaps);

cout << "upper-case letter count is " << capCount << endl;


Algorithmen für geordnete Kollektionen

13.1 Algorithmen für geordnete Kollektionen: Überblick

13.2 Sortieralgorithmen

13.3 Teilsortierung

13.4 Ntes Element

13.5 Binäre Suche

13.6 Zusammenführen geordneter Sequenzen

13.7 Mengenoperationen

13.8 Heap-Operationen


Algorithmen für geordnete Kollektionen: Überblick

Dieser Abschnitt beschreibt die generischen Algorithmen der Standardbibliothek, die sich auf geordnete Kollektionen beziehen. Die folgende Tabelle enthält eine Zusammenfassung dieser Algorithmen:

Name	Verwendung

Sortieralgorithmen

sort	Sortieren der Sequenz in korrekter
Reihenfolge

stable_sort	Sortieren unter Beibehaltung der
Originalreihenfolge gleicher Elemente

partial_sort	Sortieren eines Teils der Sequenz

partial_sort_copy	Teilsortieren in Kopie

Suchen des n-größten Elements

nth_element	Suchen des nst größten Elements

Binäre Suche

binary_search	Suchen mit booleschem Rückgabewert

lower_bound	Suchen, liefert erste Position zurück

upper_bound	Suchen, liefert letzte Position zurück

equal_range	Suchen, liefert beide Positionen zurück

Zusammenführen geordneter Sequenzen

merge	Kombinieren zweier geordneter
Sequenzen

Mengenoperationen

set_union	Vereinigungsmenge zweier Mengen

set_intersection	Schnittmenge zweier Mengen

set_difference	Differenzmenge zweier Mengen

set_symmetric_difference	symmetrische Differenz zwischen zwei Mengen

includes	Überprüfen, ob eine Menge eine
Untermenge einer anderen Menge
darstellt

Heap-Operationen

make_heap	Umwandeln einer Sequenz in einen Heap

push_heap	Hinzufügen eines neuen Werts zum Heap

pop_heap	Entfernen des größten Werts vom Heap

sort_heap	Umwandeln des Heaps in eine sortierte
Kollektion

Geordnete Kollektionen können mit der Standardbibliothek auf verschiedene Weise erzeugt werden. Zum Beispiel:

Die Container Set, Multiset, Map und Multimap sind per Definition geordnete Kollektionen.

Eine Liste kann durch Aufrufen der Elementfunktion sort() geordnet werden.

Ein Vektor, Deque oder normales C++ Array kann mit einem der später in diesem Abschnitt beschriebenen Sortieralgorithmen geordnet werden.

Wie die generischen Algorithmen, die im vorigen Abschnitt beschrieben wurden, sind die hier beschriebenen Algorithmen nicht spezifisch für eine bestimmte Container-Klasse. Sie können daher mit einer Vielzahl von Typen verwendet werden. Viele Algorithmen verlangen jedoch die Verwendung von Random Access-Iteratoren. Aus diesem Grund lassen sie sich am einfachsten mit Vektoren, Deques oder gewöhnlichen Arrays einsetzen.

Hinweis:	Die in diesem Abschnitt beschriebenen Beispielprogramme wurden für die Tutorial-Fassung in der Datei alg7.cpp zusammengestellt. Wie schon in Generische Algorithmen: Überblick wurde in der hier vorgestellten Beschreibung der Programme auf die Ausgabeanweisungen verzichtet. Sie sind jedoch in den ausführbaren Versionen enthalten.

Beinahe alle in diesem Abschnitt beschriebenen Algorithmen liegen in zwei Versionen vor. Die erste Version verwendet den zum Container-Elementtyp passenden Kleiner-Als Operator (Operator <) für Vergleiche. Die zweite, allgemeinere Version arbeitet mit einem expliziten Vergleichsfunktionsobjekt, das hier als Compare angegeben wird. Bei diesem Funktionsobjekt muß es sich um ein binäres Prädikat (siehe auch Prädikate) handeln. Da dieses Argument optional ist, wird es in der Beschreibung der Argumenttypen in eckigen Klammern dargestellt.

Eine Sequenz wird als geordnet angesehen, wenn für jeden gültigen (d.h. denotierbaren) Iterator i mit einem denotierbaren Nachfolger j der Vergleich Compare(*j, *i) false ist. Beachten Sie, daß damit Compare(*i, *j) nicht notwendigerweise true ist. Er wird davon ausgegangen, daß die durch Compare bezeichnete Relation transitiv ist und eine totale Ordnung der Werte einschließt.

In den folgenden Beschreibungen werden zwei Werte x und y als äquivalent betrachtet, wenn sowohl Compare(x, y) als auch Compare(y, x) false sind. Beachten Sie, daß dies nicht bedeutet, daß x == y ist.


Algorithmus-Include-Dateien

Ebenso wie die Algorithmen, die unter Generische Algorithmen: Überblick beschrieben wurden, können Sie die in diesem Abschnitt behandelten Algorithmen nur in einem Programm verwenden, wenn Sie zuvor die Header-Datei algorithm einbinden:

# include <algorithm>


Sortieralgorithmen

Die Standardbibliothek stellt zwei grundlegende Sortieralgorithmen zur Verfügung, die folgendermaßen beschrieben werden:

void sort (RandomAccessIterator first,

RandomAccessIterator last [, Compare ] );

void stable_sort (RandomAccessIterator first,

RandomAccessIterator last [, Compare ] );

Der Algorithmus sort() ist geringfügig schneller, garantiert jedoch nicht, daß gleiche Elemente in der Originalsequenz im Endergebnis ihre relative Ordnung behalten. Wenn die Reihenfolge wichtig ist, verwenden Sie die Version stable_sort().

Da diese Algorithmen Random Access-Iteratoren benötigen, können sie nur mit Vektoren, Deques und gewöhnlichen C Zeigern verwendet werden. Beachten Sie, daß der Listen-Container über eine eigene Elementfunktion sort() verfügt.

Der Vergleichsoperator kann explizit zur Verfügung gestellt werden, wenn der Standardoperator < nicht geeignet ist. Er wird im Beispielprogramm verwendet, um eine Liste in absteigender statt in aufsteigender Reihenfolge zu sortieren. Ein alternatives Verfahren zum Sortieren einer ganzen Kollektion in umgekehrter Reihenfolge ist die Beschreibung der Sequenz mit Umkehriteratoren.

Hinweis:	Die Heap-Operationen stellen einen weiteren Sortieralgorithmus zur Verfügung. Seine Beschreibung finden Sie unter Heap-Operationen.

Das folgende Beispielprogramm demonstriert den Algorithmus sort() für einen Vektor sowie die Verwendung eines expliziten Vergleichsoperators mit einem Deque.

void sort_example ()

// Verwendung des Algorithmus sort

{

// Füllen eines Vektors und eines Deques

// mit Zufallsintegerwerten

vector<int> aVec(15);

deque<int> aDec(15);

generate (aVec.begin(), aVec.end(), randomValue);

generate (aDec.begin(), aDec.end(), randomValue);

// Sortieren des Vektors in aufsteigender Reihenfolge

sort (aVec.begin(), aVec.end());

// Sortieren des Deques in absteigender Reihenfolge

sort (aDec.begin(), aDec.end(), greater<int>() );

// Alternatives Verfahren zum Sortieren in absteigender Reihenfolge

sort (aVec.rbegin(), aVec.rend());

}


Teilsortierung

Der generische Algorithmus partial_sort() sortiert nur einen Teil einer Sequenz. Die erste Version des Algorithmus verwendet drei Iteratoren, um Anfang, Mitte und Ende einer Sequenz anzugeben. Wenn n die Anzahl der Elemente zwischen Anfang und Mitte bezeichnet, werden die kleinsten n Elemente geordnet in diesen Bereich verschoben. Die restlichen Elemente werden in den zweiten Bereich verschoben. Die Reihenfolge der Elemente in diesem zweiten Bereich ist nicht definiert.

void partial_sort (RandomAccessIterator first,

RandomAccessIterator middle,

RandomAccessIterator last [ , Compare ]);

Eine zweite Version des Algorithmus läßt die Eingabe unverändert. Der Ausgabebereich wird durch ein Paar von Random Access-Iteratoren angegeben. Wenn n die Größe dieses Bereichs bezeichnet, werden die kleinsten n Elemente der Eingabe geordnet in die Ausgabe verschoben. Wenn n größer als die Ausgabe ist, wird die gesamte Eingabe sortiert und an die ersten n Positionen der Ausgabe gesetzt. In beiden Fällen wird das Ende der Ausgabesequenz als Ergebnis der Operation zurückgeliefert.

RandomAccessIterator partial_sort_copy

(InputIterator first, InputIterator last,

RandomAccessIterator result_first,

RandomAccessIterator result_last [, Compare ] );

Da die Eingabe für diese Version des Algorithmus nur als ein Paar von Eingabeiteratoren angegeben wird, kann der Algorithmus partial_sort_copy() mit allen Containern der Standardbibliothek verwendet werden. Im Beispielprogramm wird er mit einer Liste verwendet.

void partial_sort_example ()

// Verwendung des Algorithmus partial sort

{

// Erzeugen eines Vektors aus 15 Zufallsintegerwerten

vector<int> aVec(15);

generate (aVec.begin(), aVec.end(), randomValue);

// Teilsortierung der ersten sieben Positionen

partial_sort (aVec.begin(), aVec.begin() + 7, aVec.end());

// Liste der Zufallsintegerwerte erzeugen

list<int> aList(15, 0);

generate (aList.begin(), aList.end(), randomValue);

// Sortieren nur der ersten sieben Elemente

vector<int> start(7);

partial_sort_copy (aList.begin(), aList.end(),

start.begin(), start.end(), greater<int>());

}


Ntes Element

Angenommen, der Zentralwert, das mittlere Element, der Sequenz 2 5 3 4 7 soll ermittelt werden. Dies kann mit der Funktion nth_element() geschehen. Ein Ergebnis könnte folgende Sequenz sein:

3 2 | 4 | 7 5

Die vertikalen Trennstriche zeigen die Teilung des Ergebnisses in drei Teile an: die Elemente vor dem gefragten Wert, den gefragten Wert und die Werte nach dem gefragten Wert. Beachten Sie, daß die Werte der ersten und der dritten Sequenz ungeordnet sind. Sie können im Ergebnis in jeder beliebigen Reihenfolge auftreten. Es ist nur erforderlich, daß die Werte des ersten Teils nicht größer als der gesuchte Wert sind und die Werte des dritten Teils nicht kleiner als dieser Wert sind.

Die drei Iteratoren, die dem Algorithmus nth_element() als Argumente dienen, unterteilen die Argumentsequenz in die drei oben beschriebenen Abschnitte: den Abschnitt vor dem mittleren Iterator, den einzelnen Wert, den der mittlere Iterator angibt, und den Bereich zwischen dem mittleren Iterator und dem Ende. Der erste oder der dritte Abschnitt können leer sein.

Die Argumente des Algorithmus können folgendermaßen beschrieben werden:

void nth_element (RandomAccessIterator first,

RandomAccessIterator nth,

RandomAccessIterator last [, Compare ] );

Nach dem Aufruf von nth_element()wird der nstgrößte Wert an die Position kopiert, die der mittlere Iterator angibt. Der Bereich zwischen dem ersten und dem mittleren Iterator besitzt keine Werte, die größer als das nste Element sind, und der Bereich zwischen dem mittleren Iterator und dem Ende besitzt Werte, die nicht kleiner als das nste Element sind.

Das Beispielprogramm demonstriert, wie der fünftgrößte Wert in einem Vektor aus Zufallszahlen ermittelt wird.

void nth_element_example ()

// Verwendung des Algorithmus nth_element

{

// Erzeugen eines Vektors aus Zufallsintegerwerten

vector<int> aVec(10);

generate (aVec.begin(), aVec.end(), randomValue);

// Suchen des fünftgrößten Werts

vector<int>::iterator nth = aVec.begin() + 4;

nth_element (aVec.begin(), nth, aVec.end());

cout << "fifth largest is " << *nth << endl;

}


Binäre Suche

Die Standardbibliothek stellt viele verschiedene Varianten binärer Suchalgorithmen zur Verfügung. Alle führen nur ungefähr log n Vergleiche durch, wobei n die Anzahl der Elemente in dem Bereich ist, der durch die Argumente beschrieben wird. Die Algorithmen arbeiten am besten mit Random Access-Iteratoren, z.B. solchen, die durch Vektoren oder Deques erzeugt werden, wenn sie insgesamt ebenfalls ungefähr log n Operationen ausführen. Sie arbeiten jedoch auch mit Nicht-Random Access-Iteratoren, z.B. solchen, die durch Listen erzeugt werden. In diesem Fall führen sie eine lineare Anzahl von Schritten aus. Obwohl es zulässig ist, ist es nicht notwendig, eine binäre Suche für die Datenstrukuren Set oder Multiset durchzuführen. Diese Container-Klassen verfügen über ihre eigenen Suchmethoden, die effizienter sind.

Der generische Algorithmus binary_search() gibt true zurück, wenn die Sequenz einen Wert enthält, der dem Argument äquivalent ist. Äquivalent bedeutet, daß sowohl Compare(value, arg) als auch Compare(arg, value) false sind. Der Algorithmus ist folgendermaßen definiert:

bool binary_search (ForwardIterator first, ForwardIterator last,

const T & value [, Compare ] );

In anderen Situationen ist es vielleicht erforderlich, die Position des entsprechenden Werts zu kennen. Diese Informationen werden von einer Kollektion von Algorithmen zurückgeliefert, die folgendermaßen definiert sind:

ForwardIterator lower_bound (ForwardIterator first,

ForwardIterator last, const T& value [ , Compare ] );

ForwardIterator upper_bound (ForwardIterator first,

ForwardIterator last, const T& value [, Compare ] );

pair<ForwardIterator, ForwardIterator> equal_range

(ForwardIterator first, ForwardIterator last,

const T& value [, Compare ] );

Der Algorithmus lower_bound() gibt (wie ein Iterator) die erste Position zurück, in die das Argument eingefügt werden kann, ohne die Ordnung zu zerstören. Der Algorithmus upper_bound() hingegen sucht die letzte derartige Position. Sie stimmen nur dann überein, wenn das Element aktuell in der Sequenz nicht gefunden wurde. Beide können zusammen im Algorithmus equal_range() ausgeführt werden, der ein Iteratorenpaar zurückliefert.

Das Beispielprogramm zeigt diese Funktionen mit einem Vektor aus zufälligen Integerwerten.

void binary_search_example ()

// Verwendung des Algorithmus binary search

{

// Erzeugen eines geordneten Vektors aus 15 Zufallsintegerwerten

vector<int> aVec(15);

generate (aVec.begin(), aVec.end(), randomValue);

sort (aVec.begin(), aVec.end());

// Überprüfen, ob eine Elf enthalten ist

if (binary_search (aVec.begin(), aVec.end(), 11))

cout << "contains an 11" << endl;

else

cout << "does not contain an 11" << endl;

// eine 11 und eine 14 einfügen

vector<int>::iterator where;

where = lower_bound (aVec.begin(), aVec.end(), 11);

aVec.insert (where, 11);

where = upper_bound (aVec.begin(), aVec.end(), 14);

aVec.insert (where, 14);

}


Zusammenführen geordneter Sequenzen

Der Algorithmus merge() kombiniert zwei geordnete Sequenzen, so daß sie eine neue geordnete Sequenz bilden. Die Größe des Resultats ist die Summe der Größen der beiden Argumentsequenzen. Dieser Algorithmus steht im Gegensatz zu der Operation set_union(), die die Elemente entfernt, die in den beiden Sets doppelt sind. Die Funktion set_union() wird an späterer Stelle in diesem Abschnitt beschrieben.

Der Vorgang des Zusammenführens ist stabil. Das heißt, daß bei gleichen Elementen in den beiden Bereichen nicht nur die relative Ordnung der Werte jedes Bereichs erhalten wird, sondern auch, daß die Werte des ersten Bereichs den Elementen des zweiten Bereichs immer vorangehen. Die beiden Bereiche werden durch ein Iteratorenpaar beschrieben. Das Ergebnis wird durch einen einzelnen Ausgabeiterator definiert. Die Argumente sind wie folgt definiert:

OutputIterator merge (InputIterator first1, InputIterator last1,

InputIterator first2, InputIterator last2,

OutputIterator result [, Compare ]);

Das Beispielprogramm demonstriert eine einfache Zusammenführung, die Verwendung einer Zusammenführung mit einem Einfügeoperator und mit einem Ausgabe-Stream-Iterator.

void merge_example ()

// Verwendung des Algorithmus merge

{

// Erzeugen einer Liste und eines Vektors aus 10 Zufallsintegerwerten

vector<int> aVec(10);

list<int> aList(10, 0);

generate (aVec.begin(), aVec.end(), randomValue);

sort (aVec.begin(), aVec.end());

generate_n (aList.begin(), 10, randomValue);

aList.sort();

// Zusammenführen in einen Vektor

vector<int> vResult (aVec.size() + aList.size());

merge (aVec.begin(), aVec.end(), aList.begin(), aList.end(),

vResult.begin());

// Zusammenführen in eine Liste

list<int> lResult;

merge (aVec.begin(), aVec.end(), aList.begin(), aList.end(),

inserter(lResult, lResult.begin()));

// Zusammenführen in die Ausgabe

merge (aVec.begin(), aVec.end(), aList.begin(), aList.end(),

ostream_iterator<int> (cout, " "));

cout << endl;

}

Der Algorithmus inplace_merge() (Zusammenführen zweier benachbarter Sequenzen in einer Sequenz) kann verwendet werden, um zwei Abschnitte einer einzelnen Sequenz in eine Sequenz zusammenzuführen.


Mengenoperationen
Set-Operationen

Die Operationen Vereinigungsmenge, Schnittmenge und Differenzmenge wurden unter Mengenoperationen bei der Besprechung der Container-Klasse Set beschrieben. Die Algorithmen jedoch, die diese Operationen implementieren, sind generisch und für jede geordnete Datenstruktur verwendbar. Die Algorithmen gehen davon aus, daß es sich bei den Eingabebereichen um geordnete Kollektionen handelt, die Multisets repräsentieren, d.h. Elemente können wiederholt werden. Wenn die Eingabe jedoch Sets repräsentiert, ist auch das Ergebnis immer ein Set. Anders als der Algorithmus merge() erzeugt keiner der Set-Algorithmen in der Ausgabe wiederholte Elemente, die im Eingabeset nicht enthalten waren.

Die Set-Operationen besitzen alle das gleiche Format. Die beiden Eingabesets werden durch Eingabeiteratorenpaare angegeben. Das Ausgabeset wird durch einen Eingabeiterator angegeben, und das Ende dieses Bereichs wird als Ergebniswert zurückgeliefert. In allen Fällen ist es erforderlich, daß die Ausgabesequenz nicht auf irgendeine Art mit einer der Eingabesequenzen überlappt..

OutputIterator set_union

(InputIterator first1, InputIterator last1,

InputIterator first2, InputIterator last2,

OutputIterator result [, Compare ] );

Das Beispielprogramm demonstriert die Verwendung der vier Set-Algorithmen sowie einen Aufruf von merge(), um die Operationen Zusammenführen und Vereinigungsmenge gegenüberzustellen. Der Algorithmus includes() arbeitet etwas anders. Auch hier werden die beiden Eingabesets durch Eingabeiteratorenpaare angegeben, und der Vergleichsoperator ist ein optionales fünftes Argument. Der Rückgabewert des Algorithmus ist true, wenn das erste Set vollständig im zweiten Set enthalten ist und false, wenn dies nicht der Fall ist.

void set_example ()

// Verwendung der generischen Set-Algorithmen

{

ostream_iterator<int> intOut (cout, " ");

// Erzeugen mehrerer geordneter Listen

list<int> listOne, listTwo;

generate_n (inserter(listOne, listOne.begin()), 5, iotaGen(1));

generate_n (inserter(listTwo, listTwo.begin()), 5, iotaGen(3));

// jetzt Mengenoperationen durchführen

// Vereinigungsmenge - 1 2 3 4 5 6 7

set_union (listOne.begin(), listOne.end(),

listTwo.begin(), listTwo.end(), intOut), cout << endl;

// zusammenführen - 1 2 3 3 4 4 5 5 6 7

merge (listOne.begin(), listOne.end(),

listTwo.begin(), listTwo.end(), intOut), cout << endl;

// Schnittmenge - 3 4 5

set_intersection (listOne.begin(), listOne.end(),

listTwo.begin(), listTwo.end(), intOut), cout << endl;

// Differenzmenge - 1 2

set_difference (listOne.begin(), listOne.end(),

listTwo.begin(), listTwo.end(), intOut), cout << endl;

// symmetrische Differenz - 1 2 6 7

set_symmetric_difference (listOne.begin(), listOne.end(),

listTwo.begin(), listTwo.end(), intOut), cout << endl;

if (includes (listOne.begin(), listOne.end(),

listTwo.begin(), listTwo.end()))

cout << "set is subset" << endl;

else

cout << "set is not subset" << endl;

}


Heap-Operationen

Ein Heap stellt eine binäre Baumstruktur dar, in der jeder Knoten größer ist als die Werte, die den untergeordneten Elementen zugeordnet sind. Ein Heap (und daher auch ein binärer Baum) kann sehr effizient in einem Vektor gespeichert werden, indem die untergeordneten Elemente von Knoten i an die Positionen 2 * i + 1 und 2 * i + 2 gesetzt werden.

Durch diese Codierung wird sich der größte Wert im Heap immer an der Anfangsposition befinden. Er kann daher sehr einfach ermittelt werden. Außerdem gibt es effiziente (logarithmische) Algorithmen, die es ermöglichen, daß dem Heap eine neues Element hinzugefügt und das größte Element vom Heap entfernt wird. Aus diesen Gründen ist ein Heap eine naheliegende Darstellungsform für den Datentyp Priority Queue, der unter Datenabstraktion Priority Queue beschrieben wird. Der Standardoperator ist der Kleiner-als-Operator (Operator <), der dem Elementtyp entspricht. Wenn gewünscht, kann ein anderer Operator angegeben werden. Wenn Sie z.B. den Größer-als-Operator (Operator >) verwenden, können Sie einen Heap erzeugen, der das kleinste Element und nicht das größte an die erste Stelle setzt.

Hinweis:	Beachten Sie, daß eine geordnete Kollektion einen Heap darstellt, ein Heap aber nicht notwendigerweise eine geordnete Kollektion sein muß. Vielmehr kann ein Heap in einer Sequenz viel schneller erzeugt werden als die Sequenz sortiert werden kann.

Der Algorithmus make_heap() nimmt einen Bereich, der durch Random Access-Iteratoren angegeben wird, und wandelt ihn in einen Heap um. Die Anzahl der hierfür erforderlichen Schritte stellt eine lineare Funktion der Anzahl der Elemente im Bereich dar.

void make_heap (RandomAccessIterator first,

RandomAccessIterator last [, Compare ]);

Ein neues Element wird zu einem Heap hinzugefügt, indem es am Ende des Bereichs eingefügt wird (zum Beispiel, indem die Elementfunktion push_back() eines Vektors oder einer Deque verwendet wird), gefolgt von einem Aufruf des Algorithmus push_heap(). Der Algorithmus push_heap() stellt die Eigenschaft Heap wieder her und führt höchstens eine logarithmische Anzahl von Operationen aus.

void push_heap (RandomAccessIterator first,

RandomAccessIterator last [, Compare ]);

Der Algorithmus pop_heap() vertauscht die ersten und letzten Elemente in einem Bereich und stellt anschließend aus der Kollektion ohne das letzte Element wieder einen Heap her. Der größte Wert der Originalkollektion ist daher als letztes Element des Bereichs weiterhin verfügbar (auf diesen Wert kann beispielsweise über die Elementfunktion back() in einem Vektor zugegriffen werden, und er kann über die Elementfunktion pop_back() entfernt werden), während die restliche Kollektion weiterhin die Eigenschaft Heap besitzt. Der Algorithmus pop_heap() führt höchstens eine logarithmische Anzahl von Operationen aus.

void pop_heap (RandomAccessIterator first,

RandomAccessIterator last [, Compare ]);

Schließlich wandelt der Algorithmus sort_heap() einen Heap in eine geordnete (sortierte) Kollektion um. Beachten Sie, daß eine sortierte Kollektion noch immer ein Heap ist. Das Gegenteil ist jedoch nicht der Fall. Die Sortierung wird durch ungefähr n log n Operationen durchgeführt, wobei n die Anzahl der Elemente in Bereich darstellt. Der Algorithmus sort_heap() ist nicht stabil.

void sort_heap (RandomAccessIterator first,

RandomAccessIterator last [, Compare ]);

Das folgende Beispielprogramm demonstriert die Verwendung dieser Funktionen.

void heap_example ()

// Verwendung der Heap-Algorithmen

{

// Erzeugen eines Heaps aus 15 Zufallsintegerwerten

vector<int> aVec(15);

generate (aVec.begin(), aVec.end(), randomValue);

make_heap (aVec.begin(), aVec.end());

cout << "Largest value " << aVec.front() << endl;

// größten Wert entfernen und Heap neu erzeugen

pop_heap (aVec.begin(), aVec.end());

aVec.pop_back();

// Hinzufügen von 97 zum Heap

aVec.push_back (97);

push_heap (aVec.begin(), aVec.end());

// Umwandeln in eine sortierte Kollektion

sort_heap (aVec.begin(), aVec.end());

}


Exception-Behandlung

14.1 Exception-Behandlung: Überblick

14.2 Die Hierarchie der Standard-Exception

14.3 Verwendung von Exceptions

14.4 Beispielprogramm für die Exception-Behandlung


Exception-Behandlung: Überblick

Die C++ Standardbibliothek stellt mehrere Klassen zum Melden von Fehlern zur Verfügung. Diese Klassen verwenden die Exception-Behandlungsfunktionen der Sprache. Die Bibliothek implementiert ein bestimmtes Fehlermodell, das Fehler in zwei Kategorien aufteilt: logische Fehler und Laufzeitfehler.

Logische Fehler sind Fehler, die durch Probleme in der internen Logik des Programms hervorgerufen werden. Sie können im allgemeinen verhindert werden.

Laufzeitfehler können dagegen im allgemeinen nicht verhindert werden, da sie nicht vorhersagbar sind. Es handelt sich um Fehler, die durch Umstände erzeugt werden, die außerhalb der Kontrolle des Programms liegen, beispielsweise periphere Hardware-Fehler.


Die Hierarchie der Standard-Exceptions
Die Hierarchie der Standard-Exception

Die Bibliothek implementiert das oben beschriebene Zwei-Kategorien- Fehlermodell durch eine Menge von Klassen. Diese Klassen sind in der Header-Datei stdexcept definiert. Sie können verwendet werden, um durch die Bibliothek ausgelöste Exceptions abzufangen und um Exceptions aus Ihrem eigenen Code auszulösen.

Die Klassen stehen durch Vererbung in Beziehung. Die Vererbungshierarchie sieht folgendermaßen aus:

exception

logic_error

domain_error

invalid_argument

length_error

out_of_range

runtime_error

range_error

overflow_error

Die Klassen logic_error und runtime_error erben von der Klasse exception. Alle anderen Exception-Klassen erben entweder von logic_error oder von runtime_error.


Verwendung von Exceptions

Alle Exceptions, die explizit von einem Element der Bibliothek ausgelöst werden, sind mit Sicherheit Bestandteil der Hierarchie der Standard-Exceptions. Lesen Sie unter der Referenz für diese Klassen nach, um festzustellen, welche Funktionen welche Exceptions auslösen. Sie können wahlweise betimmte Exceptions abfangen oder alle, die ausgelöst werden (indem Sie die Basisklassen-Exception angeben).

Wenn Sie beispielsweise die Funktion insert für string mit einem Positionswert aufrufen möchten, der eventuell ungültig ist, sollte Ihr Quelltext folgendermaßen aussehen:

string s;

int n;

...

try

{

s.insert(n,"Howdy");

}

catch (const exception& e)

{

  // Exception behandeln

}

Um eigene Exceptions auszulösen, erzeugen Sie einfach eine Exception passenden Typs, weisen ihr eine entsprechende Nachricht zu und lösen sie aus. Zum Beispiel:

...

if (n > max)

  throw out_of_range("Your past the end, bud");

Die Klasse exception dient als Basisklasse für alle anderen Exception-Klassen und definiert als solche eine Standardschnittstelle. Diese Schnittstelle enthält die Elementfunktion what(), die einen nullterminierten String zurückliefert, der die Meldung repräsentiert, die mit der Exception ausgelöst wurde. Diese Funktion wird am sinnvollsten in einer Abfanganweisung eingesetzt, wie das Beispielprogramm am Ende dieses Abschnitts zeigt.

Die Klasse exception enthält keinen Konstruktor, der einen Nachrichten-String annimmt. Sie kann jedoch ohne Nachricht ausgelöst werden. Wenn Sie what() für ein Exception-Objekt aufrufen, wird eine Standardnachricht zurückgegeben. Alle von exception abgeleiteten Klasse stellen einen Konstruktor zur Verfügung, damit Sie eine bestimmte Nachricht angeben können.

Um eine Basis-Exception auszulösen, verwenden Sie den folgenden Programmtext:

throw exception;

Dies ist im allgemeinen nicht sehr sinnvoll, da beim Abfangen dieser Exception nicht klar ist, welche Art von Fehler aufgetreten ist. Statt eine Basis-Exception werden Sie daher normalerweise eine abgeleitete Klasse auslösen, beispielsweise logic_error oder eine seiner Ableitungen (etwa out_of_range, wie im Beispiel unten gezeigt). Ein noch besseres Verfahren stellt die Erweiterung der Hierarchie durch Ableitung eigener Klassen dar. Mit dieser Methode können Sie die Fehlerberichte genau an Ihr besonderes Problem anpassen. Zum Beispiel:

class bad_packet_error : public runtime_error

{

  public:

   bad_packet_error(const string& what);

};

if (bad_packet())

  throw bad_packet_error("Packet size incorrect");

Dieses Beispiel zeigt, auf welche Art die Standard C++ Exception-Klassen ein Fehlergrundmodell zur Verfügung stellen. Auf dieser Grundlage können Sie die richtige Fehlererkennungs- und Berichtsmethoden für Ihr besonderes Problem erzeugen.


Beispielprogramm: Exception-Behandlung

Hinweis:	Sie finden dieses Programm in der Datei exceptn.cpp.

Das folgende Beispielprogramm demonstriert die Verwendung von Exceptions.

#include <stdexcept>

#include <string>

static void f() { throw runtime_error("a runtime error"); }

int main ()

{

 string s;

 // Versuch, eine Exception aus der Klasse String der

 // Standardbibliothek auszulösen und abzufangen.

 // Versuch des Ersetzens an nicht vorhandener Position

//

 // Indem das Hauptprogramm von einem try-catch-Block umrahmt wird,

 // ist sichergestellt, daß alle Exceptions in der

 // Exception-Hierarchie abgefangen werden.

 // Sie können Exceptions einfach wie unten gezeigt abfangen,

 // oder Sie können jede der Exceptions abfangen, die Sie interessiert.

 try

 {

  s.replace(100,1,1,'c');

 }

 catch (const exception& e)

 {

  cout << "Got an exception: " << e.what() << endl;

 }

 // Jetzt wird mit der oben definierten Funktion eine

 // eigene Exception ausgelöst.

 try

 {

  f();

 }

 catch (const exception& e)

 {

  cout << "Got an exception: " << e.what() << endl;

 }

 return 0;

}


auto_ptr

15.1 Überblick

15.2 Erzeugen und Verwenden von Autozeigern

15.3 Beispielprogramm


auto_ptr: Überblick

Die Klasse auto_ptr umrahmt jeden Zeiger, den new liefert und stellt automatisches Löschen dieses Zeigers zur Verfügung. Der Zeiger, der von einem Objekt auto_ptr umrahmt wird, wird beim Entfernen von auto_ptr selbst gelöscht.


Erzeugen und Verwenden von Autozeigern

Binden Sie die Utility-Header-Datei ein, um auf die Klasse auto_ptr zuzugreifen.

Sie verbinden ein Objekt auto_ptr mit einem Zeiger, indem Sie entweder einen der Konstruktoren für auto_ptr verwenden oder ein Objekt auto_ptr einem anderen zuweisen oder, indem Sie die Elementfunktion reset verwenden. Nur ein auto_ptr "besitzt" einen bestimmten Zeiger zu einem gegebenen Zeitpunkt. Eine Ausnahme bildet der Zeiger NULL (den alle auto_ptrs standardmäßig besitzen). Jede Verwendung von auto_ptrs Kopierkonstruktor oder seines Zuweisungsoperators überträgt den Besitz von einem Objekt auto_ptr auf ein anderes. Angenommen, auto_ptr a wird folgendermaßen erzeugt:

auto_ptr<string> a(new string);

Das auto_ptr-Objekt a "besitzt" nun den neu erzeugten Zeiger. Wenn a entfernt wird (etwa, weil der Gültigkeitsbereich überschritten wird), wird der Zeiger gelöscht. Wenn man a mit dem Zuweisungsoperator b zuweist:

auto_ptr<string> b = a;

besitzt jetzt b den Zeiger. Die Verwendung des Zuweisungsoperators veranlaßt a, den Besitzt des Zeigers aufzugeben. Wenn nun a den Gültigkeitsbereich verläßt, wirkt sich dies nicht auf den Zeiger aus. Der Zeiger wird jedoch gelöscht, wenn b den Gültigkeitsbereich verläßt.

Die Verwendung von new im Konstruktor für a mutet vielleicht etwas seltsam an. Normalerweise sollten solche Konstrukte vermieden werden, da die Verantwortung für das Löschen bei einer anderen Instanz liegt, als die Verantwortung für die Zuweisung. In diesem Fall jedoch muß auto_ptr nur den Löschvorgang verwalten. Diese Syntax ist hier tatsächlich vorzuziehen, da sie verhindert, daß der Anwender selbst den Zeiger versehentlich löscht.

Verwenden Sie operator*, operator-> oder die Elementfunktion get(), um auf den Zeiger eines auto_ptrs zuzugreifen. Beispielsweise kann jede der drei folgenden Anweisungen verwendet werden, um "What's up Doc" dem String zuzuweisen, auf den nun der auto_ptr b zeigt.

*b = "What's up Doc";

*(b.get()) = "What's up Doc";

 b->assign("What's up Doc");

auto_ptr stellt außerdem eine Elementfunktion release zur Verfügung, die einen Zeiger freigibt. Es wird angenommen, daß jeder auto_ptr, der keinen bestimmten Zeiger besitzt, auf den Zeiger NULL zeigt, so daß der Aufruf von release für ein auto_ptr diesen auf den NULL-Zeiger setzt. Im Beispiel oben wird bei der Zuweisung von a zu b der Zeiger von a auf den NULL-Zeiger gesetzt.


Beispielprogramm: auto_ptr

Dieses Programm demonstriert, wie auto_ptr verwendet wird, um sicherzustellen, daß die Zeiger eines Vektors auch gelöscht werden, wenn sie entfernt werden. Häufig wird bei Strings mit Zeigern gearbeitet, da die Strings selbst recht groß sein können und sie für den Vektor kopiert werden. Besonders im Vergleich zu einem String ist ein auto_ptr recht klein: er ist kaum größer als ein Zeiger.

Hinweis:	Sie finden dieses Programm in der Datei autoptr.cpp.

#include <vector>

#include <utility>

#include <string>

int main()

{

 {

  // Dies ist das falsche Verfahren

  vector<string*> v;

  v.insert(v.begin(), new string("Florence"));

  v.insert(v.begin(), new string("Milan"));

  v.insert(v.begin(), new string("Venice"));

  // Entfernen des ersten Elements

  v.erase(v.begin());

 // Hoppla, Speicherleck

  // Der String("Venice") wurde entfernt, aber nicht gelöscht

  // Es wird erwartet, daß sich der Anwender darum kümmert

 }

 {

  // So ist es richtig

  vector<auto_ptr<string> > v;

  v.insert(v.begin(),

       auto_ptr<string>(new string("Florence")));

  v.insert(v.begin(), auto_ptr<string>(new string("Milan")));

  v.insert(v.begin(), auto_ptr<string>(new string("Venice")));

  // Alles in Ordnung, da auto_ptrs beim Kopieren den
  // Besitz ihrer Zeiger übertragen hat

  // Entfernen des ersten Elements

  v.erase(v.begin());

  // Erfolg

  // Wenn auto_ptr(String("Venice")) gelöscht wird (und entfernt wird),

  // wird der String("Venice") gelöscht

 }

 return 0;

}


Complex

16.1 Complex: Überblick

16.2 Erzeugen und Verwenden komplexer Zahlen

15.3 Beispielprogramm


Complex: Überblick

Bei der Klasse complex handelt es sich um ein Klassen-Template, das verwendet wird, um Objekte zur Darstellung und Manipulation komplexer Zahlen zu erzeugen. Die Operationen, die für komplexe Zahlen definiert sind, ermöglichen es, sie frei mit den anderen in C++ verfügbaren numerischen Typen zu mischen. Hierdurch kann numerische Software einfach und natürlich programmiert werden.


Erzeugen und Verwenden komplexer Zahlen

Die folgenden Abschnitte beschreiben die Operationen, die zum Erzeugen und Manipulieren komplexer Zahlen verwendet werden.


Header-Dateien

Programme, die mit komplexen Zahlen arbeiten, müssen die Header-Datei complex einbinden.

# include <complex>


Deklarieren komplexer Zahlen

Das Argument template wird verwendet, um die mit den reellen und imaginären Feldern verbundenen Typen zu definieren. Dieses Argument muß einer der Gleitkomma-Zahlentypen sein, die in der Sprache C++ verfügbar sind, also entweder float, double oder long double.

Die Klasse arbeitet mit einer Reihe von Konstruktoren. Ein Konstruktor ohne Argumente initialisiert sowohl die reellen als auch die imaginären Felder mit Null. Ein Konstruktor mit einem Argument initialisiert das reelle Feld mit dem gegebenen Wert und den Wert des imaginären Feldes mit Null. Ein Konstruktor mit zwei Argumenten initialisiert das reelle und das imaginäre Feld. Schließlich kann ein Kopierkonstruktor verwendet werden, um eine komplexe Zahl mit den Werten, die aus einer anderen komplexen Zahl abgeleitet werden, zu initialisieren.

complex<double> com_one;	// Wert 0 + 0i

complex<double> com_two(3.14);	// Wert 3.14 + 0i

complex<double> com_three(1.5, 3.14)	// Wert 1.5 + 3.14i

complex<double> com_four(com_two);	// Wert ist auch 3.14 + 0i

Eine komplexe Zahl kann als Wert einer anderen komplexen Zahl zugewiesen werden. Da der Ein-Argument-Konstruktor auch als Umwandlungsoperator dient, kann einer komplexen Zahl auch der Wert einer reellen Zahl zugewiesen werden. Das reelle Feld wird auf der rechten Seite geändert, während das imaginäre Feld auf Null gesetzt wird.

com_one = com_three;	// wird zu 1.5 + 3.14i

com_three = 2.17;	// wird zu 2.17 + 0i

Die Funktion polar() kann verwendet werden, um eine komplexe Zahl mit gegebener Größe und gegebenem Phasenwinkel zu erzeugen.

com_four = polar(5.6, 1.8);

Die Konjugation einer komplexen Zahl wird unter Verwendung der Funktion conj() gebildet. Wenn eine komplexe Zahl x + yi darstellt, ist die Konjugation der Wert y + xi.

complex<double> com_five = conj(com_four);


Zugriff auf komplexe Zahlenwerte

Die Elementfunktionen real() und imag() liefern die reellen bzw. imaginären Felder einer komplexen Zahl zurück. Diese Funktionen können auch als gewöhnliche Funktionen mit komplexen Zahlen als Argumenten aufgerufen werden.

Hinweis:	Beachten Sie, daß mit Ausnahme der Elementfunktionen real() und imag() die meisten Operationen mit komplexen Zahlen über gewöhnliche Funktionen und nicht über Elementfunktionen durchgeführt werden.

// Folgendes sollte gleich sein

cout << com_one.real() << "+" << com_one.imag() << "i" << endl;

cout << real(com_one) << "+" << imag(com_one) << "i" << endl;


Arithmetische Operationen

Die arithmetischen Operatoren +, -, * und / können für die Addition, Subtraktion, Multiplikation und Division komplexer Zahlen verwendet werden. Alle vier arbeiten entweder mit zwei komplexen Zahlen oder mit einer komplexen Zahl und einem reellen Wert. Zuweisungsoperatoren sind ebenfalls für alle vier definiert.

cout << com_one + com_two << endl;

cout << com_one - 3.14 << endl;

cout << 2.75 * com_two << endl;

com_one += com_three / 2.0;

Die unären Operatoren + und - können ebenfalls mit komplexen Zahlen verwendet werden.


Vergleichen komplexer Werte

Zwei komplexe Zahlen können auf Gleichheit oder Ungleichheit verglichen werden, und zwar mit den Operatoren == und !=. Zwei Werte sind gleich, wenn ihre entsprechenden Felder gleich sind. Komplexe Zahlen sind nicht wohlgeordnet und können daher nicht mit einem anderen relationalen Operator verglichen werden.


Stream-Eingabe und -Ausgabe

Komplexe Zahlen können unter Verwendung der normalen Stream-E/A-Konventionen in einen Ausgabe-Stream geschrieben werden oder aus einem Eingabe-Stream gelesen werden. Ein Wert wird in Klammern angegeben, entweder als (u) oder (u,v), abhängig davon, ob der imaginäre Wert Null ist oder nicht. Ein Wert wird als Klammer gelesen, die zwei numerische Werte umschließt.


Normal- und Absolutwert

Die Funktion norm() gibt das Normal der komplexen Zahl zurück. Es ist die Summe der Quadrate des reellen und des imaginären Teils. Die Funktion abs() gibt den absoluten Wert zurück, der die Quadratwurzel des Normals ist. Beachten Sie, daß beides gewöhnliche Funktionen sind, die den komplexen Wert als Argument annehmen, nicht Elementfunktionen.

cout << norm(com_two) << endl;

cout << abs(com_two) << endl;

Der gerichtete Phasenwinkel einer komplexen Zahl wird von der Funktion arg() zurückgeliefert.

cout << com_four << " in polar coordinates is "

<< arg(com_four) << " and " << norm(com_four) << endl;


Trigonometrische Funktionen

Die trigonometrischen Funktionen, die für Gleitkommawerte definiert sind (nämlich sin(), cos(), tan(), asin(), acos(), atan(), sinh(), cosh() und tanh()), wurden alle zu Argumenten für komplexe Zahlen erweitert. Jede Funktion nimmt eine einzelne komplexe Zahl als Argument an und gibt eine komplexe Zahl als Ergebnis zurück. Die Funktion atan2() nimmt zwei komplexe Zahlen als Argumente an oder eine komplexe Zahl und einen reellen Wert (in beliebiger Reihenfolge) und liefert als Ergebnis eine komplexe Zahl zurück.


Transzendente Funktionen

Die transzendenten Funktionen exp(), log(), log10() und sqrt() wurden zu komplexen Argumenten erweitert. Jede Funktion nimmt eine einzelne komplexe Zahl als Argument an und liefert eine komplexe Zahl als Ergebnis zurück.

Die Standardbibliothek definiert mehrere Varianten der Exponentialfunktion pow(). Mit den verschiedenen Versionen kann man eine komplexe Zahl in eine ganzzahlige Potenz erheben, eine komplexe Zahl in eine komplexe Potenz oder eine reelle Potenz, oder einen reellen Wert in eine komplexe Potenz.


Beispielprogramm: Wurzeln eines Polynoms

Hinweis:	Dieses Programm finden Sie in der Datei complx.cpp.

Die Wurzeln eines Polynoms a x2 + b x + c = 0 sind durch die Formel

x = (-b ± sqrt(b2 - 4ac))/2a

gegeben. Das folgende Programm nimmt als Eingabe drei double precision Zahlen und gibt die komplexen Wurzeln als Wertepaar zurück.

typedef complex<double> dcomplex;

pair<dcomplex, dcomplex> quadratic

(dcomplex a, dcomplex b, dcomplex c)

// Wurzeln einer quadratischen Gleichung zurückliefern

{

dcomplex root = sqrt(b * b - 4.0 * a * c);

a *= 2.0;

return make_pair(

(-b + root)/a,

(-b - root)/a);

}


String

15.1 Die Abstraktion String

15.2 String-Operationen

15.3 Eine Beispielfunktion


Die Abstraktion String

Ein String ist eigentlich eine indizierbare Sequenz von Zeichen. Daher können, obwohl ein String nicht als Vektorunterklasse deklariert ist, beinahe alle in Vektoroperationen beschriebenen Vektoroperatoren auf String-Werte angewendet werden. Ein String ist jedoch auch eine wesentlich abstraktere Größe. Zusätzlich zu einfachen Vektoroperationen stellt der Datentyp String viele sinnvolle und leistungsfähige Operationen auf hoher Ebene zur Verfügung.

In der Standardbibliothek ist ein String ein Klassen-Template namens basic_string. Das Template-Argument stellt den Zeichentyp dar, den der String-Container enthalten wird. Indem Strings auf diese Art definiert werden, stellt die Standardbibliothek nicht nur Möglichkeiten zur Manipulation von Sequenzen aus normalen 8-Bit-ASCII-Zeichen zur Verfügung, sondern auch für andere Typen zeichenähnlicher Sequenzen, beispielsweise für 16-Bit-Zeichen. Die Datentypen string und wstring (für wide string) sind einfach Typdefinitionen von basic_string, die folgendermaßen definiert sind:

typedef basic_string<char,strint_char_traits<char> > string;

typedef basic_string<wchar_t> wstring;

Hinweis:	Der Rest dieses Abschnitts bezieht sich auf den Datentyp string. Alle vorgestellten Operationen sind jedoch auf gleiche Weise für wide strings verwendbar.

Wie bereits angemerkt wurde, gleicht ein String in vielem einem Vektor aus Zeichen. Wie auch beim Datentyp Vektor sind zwei Größenangaben mit einem String verbunden. Die erste stellt die Anzahl der Zeichen dar, die aktuell im String gespeichert werden. Die zweite Größe ist die Kapazität, die größte Zeichenzahl, die potentiell in einem String gespeichert werden kann, ohne einen neuen internen Puffer zuzuweisen. Genau wie beim Datentyp Vektor stellt die Kapazität eines Strings eine dynamische Größe dar. Wenn durch String-Operationen die Anzahl der Zeichen, die in einem String-Wert gespeichert werden, die Kapazität des Strings übersteigt, wird ein neuer interner Puffer zugewiesen und mit den String-Werten initialisiert, und die Kapazität des Strings wird hierdurch vergrößert. Diese Aktionen finden hinter den Kulissen statt und erfordern keine Interaktion mit dem Programmierer.


String-Include-Dateien

Programme, die mit Strings arbeiten, müssen die Header-Datei string einbinden:

# include <string>


String-Operationen

Die folgenden Abschnitte untersuchen die Operationen der Standardbibliothek zum Erzeugen und Manipulieren von Strings.


Deklarieren von String-Variablen

Die einfachste Deklarationsform für einen String benennt einfach nur eine neue Variable oder benennt eine Variable zusammen mit dem Initialwert für einen String. Von diesem Verfahren wurde im Beispielprogramm Graph, das Sie unter Beispielprogramm: Graphs. finden, ausgiebig Gebrauch gemacht. Mit einem Kopierkonstruktor können Sie einen String deklarieren, der seinen Wert von einem zuvor definierten String übernimmt.

string s1;

string s2 ("a string");

string s3 = "initial value";

string s4 (s3);

In diesen einfachen Fällen ist die Kapazität ursprünglich genau gleich der Anzahl der gespeicherten Zeichen. Mit alternativen Konstruktoren können Sie die Anfangskapazität explizit setzen. Eine weitere Form ermöglicht es, die Kapazität zu setzen und den String mit wiederholten Kopien eines einzelnen Zeichenwerts zu initialisieren.

string s6 ("small value", 100);	// speichert 11 werte, kann 100 speichern

string s7 (10, '\n');	// speichert zehn Zeilenvorschubzeichen

Hinweis:	Denken Sie daran, daß für das Initialisieren eines Containers mit einem Iteratorenpaar die Deklaration einer Template-Elementfunktion mit Template-Argumenten, die unabhängig von denen sind, mit denen der Container deklariert wird, erforderlich ist. Zur Zeit unterstützen nicht alle Compiler diese Möglichkeit.

Schließlich kann ein String, wie alle Container-Klassen in der Standardbibliothek, mit einem Iteratorenpaar initialisiert werden. Die durch die Iteratoren bezeichnete Sequenz muß die passenden Elementtypen besitzen.

string s8 (aList.begin(), aList.end());


Setzen von Größe und Kapazität

Genau wie beim Datentyp Vektor wird die aktuelle Größe des Strings von der Elementfunktion size() zurückgegeben. Die aktuelle Kapazität liefert capacity() zurück. Die Kapazität kann durch einen Aufruf der Elementfunktion reserve() geändert werden, die (wenn erforderlich) die Kapazität so anpaßt, daß der String mindestens so viele Elemente enthalten kann, wie durch das Argument angegeben werden. Die Elementfunktion max_size() gibt die maximale String-Größe zurück, die zugewiesen werden kann. Normalerweise wird dieser Wert nur durch den verfügbaren Speicher eingeschränkt.

cout << s6.size() << endl;

cout << s6.capacity() << endl;

s6.reserve(200);	// ändere Kapazität zu 200

cout << s6.capacity() << endl;

cout << s6.max_size() << endl;

Die Elementfunktion length() ist nur ein Synonym für size(). Die Elementfunktion resize() ändert die Größe eines Strings, indem sie entweder Zeichen am Ende abschneidet oder neue Zeichen einfügt. Das optionale zweite Argument für resize() kann zur Angabe des Zeichens verwendet werden, das in die neu erzeugten Zeichenpositionen eingefügt werden soll.

s7.resize(15, '\t');	// Tabulatorenzeichen am Ende hinzufügen

cout << s7.length() << endl;	// Größe sollte nun 15 betragen

Die Elementfunktion empty() gibt true zurück, wenn der String keine Zeichen enthält. Es ist meistens schneller diese Elementfunktion zu verwenden, statt die Länge mit einer Nullkonstanten zu testen.

if (s7.empty())

cout << "string is empty" << endl;


Zuweisen, Anhängen und Vertauschen

Einer String-Variable kann der Wert eines anderen Strings, ein literales Zeichen-Array im C-Stil oder ein bestimmtes Zeichen zugewiesen werden.

s1 = s2;

s2 = "a new value";

s3 = 'x';

Der Operator += kann ebenfalls mit diesen drei Argumentformen verwendet werden. Er gibt an, daß der Wert auf der rechten Seite am Ende des aktuellen String-Werts angehängt werden soll.

s3 += "yz";	// s3 ist jetzt xyz

Mit den allgemeineren Elementfunktionen assign() und append() können Sie eine Untermenge der Werte auf der rechten Seite angeben, die dem Empfänger zugewiesen oder an ihn angehängt werden sollen. Ein einzelnes Integer-Argument n gibt an, daß nur die ersten n Zeichen zugewiesen/angehängt werden sollen. Zwei Argumente hingegen, pos und n, geben an, daß die n Werte, die auf die Position pos folgen, verwendet werden sollen.

s4.assign (s2, 3);	// die ersten drei Zeichen zuweisen

s4.append (s5, 2, 3);	// Zeichen 2, 3 und 4 anhängen

Der Additionsoperator + wird verwendet, um zwei Strings zu verketten. Der Operator + erzeugt eine Kopie des linken Arguments und hängt anschließend das rechte Argument an diesen Wert an.

cout << (s2 + s3) << endl;	// Ausgabeverkettung von s2 und s3

Wie bei allen Containern in der Standardbibliothek kann der Inhalt zweier Strings mit der Elementfunktion swap() vertauscht werden.

s5.swap (s4);	// Vertauschen von s4 und s5


Zeichenzugriff

Auf ein einzelnes Zeichen aus einem String kann mit dem Indizierungsoperator  zugegriffen werden, oder es kann über ihn zugewiesen werden. Die Elementfunktion at() ist ein Synonym für diese Operation.

cout << s4[2] << endl;	// Ausgabe Position 2 von s4

s4[2] = 'x';	// Position 2 ändern

cout << s4.at(2) << endl;	// Ausgabe des aktualisierten Werts

Die Elementfunktion c_str() gibt einen Zeiger auf ein nullterminiertes Zeichen-Array zurück, dessen Elemente gleich den im String enthaltenen Elementen sind. Hierdurch können Sie Strings mit Funktionen verwenden, die einen Zeiger auf ein konventionelles Zeichen-Array im C-Stil erfordern. Der sich ergebende Zeiger wird als konstant deklariert. Dies bedeutet, daß Sie nicht c_str() verwenden können, um den String zu ändern. Außerdem ist der von c_str() zurückgegebene Wert nach Operationen, die eine Realloziierung bewirken (wie append() oder insert()), möglicherweise nicht gültig. Die Elementfunktion data() gibt einen Zeiger auf den zugrundeliegenden Zeichenpuffer zurück.

char d[256];

strcpy(d, s4.c_str());	// Kopieren von s4 in Array d


Iteratoren

Die Elementfunktionen begin() und end() geben Anfangs- und End-Random-Access-Iteratoren für den String zurück. Die durch die Iteratoren bezeichneten Werte sind einzelne String-Elemente. Die Funktionen rbegin() und rend() liefern Rückwärtsiteratoren zurück.

Hinweis:	Beachten Sie, daß der Inhalt eines Iterators nicht notwendigerweise gültig ist, nachdem eine Operation durchgeführt wurde, die eine Realloziierung eines internen String-Puffers, wie Append oder eine Einfügung, erzwingt.


Einfügen, Entfernen und Ersetzen

Die String-Elementfunktionen insert() und remove() ähneln den Vektorfunktionen insert() und erase(). Wie die Vektorversionen können sie Iteratoren als Argumente annehmen und das Einfügen oder Entfernen der durch die Argumente bezeichneten Bereiche angeben. Die Funktion replace() stellt eine Kombination aus remove und insert dar und ersetzt den angegebenen Bereich durch neue Werte.

s2.insert(s2.begin()+2, aList.begin(), aList.end());

s2.remove(s2.begin()+3, s2.begin()+5);

s2.replace(s2.begin()+3, s2.begin()+6, s3.begin(), s3.end());

Außerdem besitzen die Funktionen Implementationen, die nicht mit Iteratoren arbeiten. Die Elementfunktion insert() nimmt als Argument eine Position und einen String an und fügt den String an der angegebenen Position ein. Die Funktion remove() nimmt zwei Integer-Argumente an, eine Position und eine Länge, und entfernt die angegebenen Zeichen. Die Funktion replace() schließlich nimmt zwei ähnliche Integer-Argumente sowie einen String und eine optionale Länge an und ersetzt den angegebenen Bereich durch einen String (oder durch den Anfangsteil eines Strings, wenn die Länge explizit angegeben wurde).

s3.insert (3, "abc");	//abc nach Position 3 einfügen

s3.remove (4, 2);	// Positionen 4 und 5 entfernen

s3.replace (4, 2, "pqr");	//Positionen 4 und 5 durch pqr ersetzen


Kopieren und Substring

Die Elementfunktion copy() erzeugt einen Substring des Empfängers und weist diesen Substring anschließend dem Ziel zu, das als erstes Argument angegeben wurde. Der Wertebereich für den Substring wird entweder durch eine Anfangsposition oder durch eine Position und eine Länge angegeben.

s3.copy (s4, 2);	// s4 Positionen 2 bis Ende von s3 zuweisen

s5.copy (s4, 2, 3);	// s4 Positionen 2 bis 4 von s5 zuweisen

Die Elementfunktion substr() gibt einen String zurück, der einen Teil des aktuellen Strings darstellt. Der Bereich wird entweder durch eine Anfangsposition oder durch eine Position und eine Länge angegeben.

cout << s4.substr(3) << endl;	// Ausgabe 3 bis Ende

cout << s4.substr(3, 2) << endl;	// Ausgabe Positionen 3 und 4


String-Vergleiche

Hinweis:	Obwohl der Zugriff auf die Funktion möglich ist, werden Anwender die Elementfunktion compare() selten direkt aufrufen. Normalerweise werden String-Vergleiche statt dessen mit den konventionellen Vergleichsoperatoren durchgeführt, die ihrerseits die Funktion compare() verwenden.

Die Elementfunktion compare() wird verwendet, um einen lexikalischen Vergleich zwischen dem Empfänger und einem Argument-String durchzuführen. Optionale Argumente erlauben die Angabe einer anderen Startposition oder einer Startposition und der Länge des Argument-Strings. Vergleichen Sie Lexikalischer Vergleich, um sich über die lexikalische Reihenfolge zu informieren. Die Funktion gibt einen negativen Wert zurück, wenn der Empfänger lexikographisch kleiner als das Argument ist, einen Nullwert, wenn beide gleich sind, und einen positiven Wert, wenn der Empfänger größer als das Argument ist.

Die relationalen und Gleichheitsoperatoren (<, <=, ==, !=, >= und >) sind alle über die Vergleichselementfunktion definiert. Vergleiche können entweder zwischen zwei Strings durchgeführt werden oder zwischen Strings und gewöhnlichen Zeichenliteralen im C-Stil.


Suchoperationen

Die Elementfunktion find() bestimmt das erste Auftreten des Argument-Strings im aktuellen String. Ein optionales Integer-Argument ermöglicht die Angabe der Startposition für die Suche. (Denken Sie daran, daß String-Index-Positionen bei Null beginnen.) Wenn die Funktion eine solche Entsprechung finden kann, liefert sie den Startindex der Entsprechung im aktuellen String zurück. Andernfalls gibt sie einen Wert aus dem Bereich der Menge zulässiger Indizes des Strings zurück. Die Funktion rfind() ist ähnlich, untersucht den String jedoch vom Ende her und bewegt sich rückwärts.

s1 = "mississippi";

cout << s1.find("ss") << endl;	// gibt 2 zurück

cout << s1.find("ss", 3) << endl; 	// gibt 5 zurück

cout << s1.rfind("ss") << endl;	// gibt 5 zurück

cout << s1.rfind("ss", 4) << endl;  	// gibt 2 zurück

Die Funktionen find_first_of(), find_last_of(), find_first_not_of(), und find_last_not_of() behandeln den Argument-String als Zeichenmenge. Wie bei vielen der anderen Funktionen können ein oder zwei optionale Integer-Argumente verwendet werden, um eine Untermenge des aktuellen Strings anzugeben. Diese Funktionen suchen das erste (oder das letzte) Zeichen, das in der Argumentmenge entweder vorhanden oder nicht vorhanden ist. Die Position des angegebenen Zeichens wird zurückgeliefert, wenn es gefunden wurde. Wenn ein solches Zeichen nicht vorhanden ist, wird ein Wert aus dem Bereich der Menge zulässiger Indizes zurückgegeben.

i = s2.find_first_of ("aeiou");	// Suchen des ersten Vokals

j = s2.find_first_not_of ("aeiou", i);	// Nächster Nicht-Vokal


Beispielfunktion: Aufteilen einer Zeile in Wörter

Hinweis:	Die Funktion split finden Sie im Konkordanz-Programm in der Datei concord.cpp.

In diesem Abschnitt wird die Verwendung einiger String-Funktionen demonstriert. Es wird eine Funktion definiert, die eine Textzeile in einzelne Wörter zerlegt. Diese Funktion wurde bereits im Beispielsprogramm für eine Konkordanz verwendet, das Sie unter Beispielprogramm: Eine Konkordanz finden.

Die Funktion nimmt drei Argumente an. Bei den beiden ersten handelt es sich um Strings, die die Textzeile bzw. die Trennzeichen beschreiben, die die Wörter voneinander trennen. Das dritte Argument ist eine Liste von Strings, die verwendet wird, um die einzelnen Wörter der Zeile zurückzugeben.

void split

(string & text, string & separators, list<string> & words)

{

int n = text.length();

int start, stop;

start = text.find_first_not_of(separators);

while ((start >= 0) && (start < n)) {

stop = text.find_first_of(separators, start);

if ((stop < 0) || (stop > n)) stop = n;

words.push_back(text.substr(start, stop - start));

start = text.find_first_not_of(separators, stop+1);

}

}

Das Programm sucht zuerst das erste Zeichen, das kein Trennzeichen ist. Die Schleife sucht anschließend nach dem nächsten folgenden Zeichen, bei dem es sich um ein Trennzeichen handelt, oder verwendet das Ende des Strings, wenn ein solcher Wert nicht gefunden wird. Die Differenz zwischen diesen beiden ist ein Wort, und dieses Wort wird mit einer Substring-Operation aus dem Text kopiert und in die Liste der Wörter eingefügt. Die Suche richtet sich anschließend auf den Anfang des nächsten Worts, und die Schleife wird wiederholt. Wenn der Indexwert die Grenzen des Strings übersteigt, wird die Ausführung angehalten.


Numerische Grenzen

16.1 Numerische Grenzen: Überblick

16.2 Grundlegende Datentypen

16.3 Elemente von numeric_limit


Numerische Grenzen: Überblick

Ein neues Merkmal der C++ Standardbibliothek besteht in einem organisierten Mechanismus zur Beschreibung der Charakteristika der grundlegenden Typen, die in der Ausführungsumgebung verfügbar sind. In älteren C und C++ Bibliotheken wurden diese Charakteristika oft durch große Kollektionen symbolischer Konstanten  beschrieben. Beispielsweise fand sich der kleinste darstellbare Wert, den ein Zeichen enthalten konnte, in einer Konstante namens CHAR_MIN, während die ähnliche Konstante für short SHRT_MIN hieß, für float FLT_MIN, usw.

Hinweis:	Aus Kompatibilitätsgründen wird der Mechanismus numeric_limits zusätzlich zu den in den älteren C++ Bibliotheken verwendeten symbolischen Konstanten benutzt, nicht als Ersatz für diese. Beide Mechanismen werden zur Zeit nebeneinander verwendet. Da die Technik numeric_limits jedoch einheitlicher und leichter zu erweitern ist, werden die älteren symbolischen Konstanten jedoch im Lauf der Zeit wahrscheinlich durch sie ersetzt werden.

Das Klassen-Template numeric_limits stellt ein neues und einheitliches Verfahren zur Darstellung dieser Informationen für alle numerischen Typen zur Verfügung. Anstatt einen unterschiedlichen symbolischen Namen für jeden neuen Datentyp zu verwenden, definiert die Klasse eine einzige statische Funktion namens min(), die die passenden Werte zurückliefert. Spezialformen dieser Klasse liefern anschließend den exakten Wert für jeden unterstützten Typ. Der kleinste Zeichenwert wird auf diese Art als Ergebnis des Aufrufs der Funktion numeric_limits<char>::min() zurückgegeben, während der kleinste Gleitkommawert durch Aufruf von numeric_limits<float>::min()ermittelt wird, usw.

Indem man dieses Problem durch ein Klassen-Template löst, wird nicht nur die Anzahl der symbolischen Namen, die zur Beschreibung der Betriebssystemumgebung definiert werden müssen, erheblich reduziert, sondern auch die Konsistenz der Beschreibungen der verschiedenen Typen gewährleistet.


Grundlegende Datentypen

Die Standardbibliothek beschreibt einen bestimmten Typ, indem sie eine spezialisierte Implementierung der Klasse numeric_limits für diesen Typ zur Verfügung stellt. Statische Funktionen und statische konstante Datenelemente liefern anschließend für den Typ spezifische Informationen. Die Standardbibliothek enthält Beschreibungen der folgenden grundlegenden Datentypen.

bool	char	int	float

  	signed char	short	double

unsigned char	long	long double

wchar_t	unsigned short

unsigned int

unsigned long

Bestimmte Implementierungen liefern möglicherweise Informationen zu anderen Datentypen. Ob eine Implementierung beschrieben wird, kann über das statische Datenelementfeld is_specialized festgestellt werden. Folgendes ist beispielsweise zulässig und gibt an, daß der String-Datentyp durch diesen Mechanismus nicht beschrieben wird.

cout << "are strings described " <<

numeric_limits<string>::is_specialized << endl;

Bei Datentypen, die keine spezialisierte Implementierung besitzen, sind die Werte, die durch die Funktionen und Datenfeldern in numeric_limits zurückgegeben werden, im allgemeinen Null oder false.


Elemente von numeric_limit

Da viele Felder in der Struktur numeric_limits nur für Gleitkommawerte von Bedeutung sind, ist es sinnvoll, die Beschreibung der Elemente in allgemeine Felder und spezifische Gleitkommafelder zu unterteilen.


Elemente, die allen Typen gemeinsam sind

Die folgende Tabelle faßt die Informationen zusammen, die über die statischen Elementdatenfelder und Funktionen von numeric_limits verfügbar sind.

Typ	Name	Bedeutung

bool	is_specialized	true, wenn Spezialisierung
vorhanden ist, ansonsten false

T	min()	kleinster finiter Wert

T	max()	größter finiter Wert

int	radix	Basis der Darstellung

int	digits	Anzahl von radix Stellen, die
ohne Änderung dargestellt werden
können

int	digits10	Anzahl von Basis-10 Stellen, die
ohne Änderung dargestellt werden
können

bool	is_signed	true, wenn es sich um einen vorzeichenbehafteten Typ handelt

bool	is_integer	true, wenn der Typ integer ist

bool	is_exact	true, wenn die Darstellung exakt ist

bool	is_bounded	true, wenn die Darstellung finit ist

bool	is_modulo	true, wenn der Typ modulo ist

bool	traps	true, wenn Aufspüren für den Typ
implementiert ist

Radix stellt die interne Basis für die Darstellung dar. Die meisten Rechner verwenden einen Basis-2-Radix für Integerdatenwerte. Einige Rechner unterstützen jedoch Darstellungen, die andere Basen verwenden, beispielsweise BCD. Das Feld digits stellt die Anzahl dieser Radix-Werte, die ein Wert enthalten kann, dar. Bei einem Integertyp ist dies die Anzahl der vorzeichenlosen Bits in der Darstellung.

Alle grundlegenden Typen sind begrenzt. Eine Implementation kann jedoch zum Beispiel wahlweise ein infinites Precision-Integer-Paket, das nicht begrenzt ist, enthalten.

Ein Typ ist modulo, wenn der Wert, der sich aus der Addition zweier Werte ergibt, kleiner als beide Argumente ist. Die grundlegenden vorzeichenlosen Integertypen sind alle modulo.


Spezifische Elemente für Gleitkommawerte

Die folgenden Elemente sind entweder spezifisch für Gleitkommawerte oder besitzen eine Bedeutung, die sich etwas von der vorherigen Beschreibung der Nicht-Gleitkomma-Datentypen unterscheidet.

Typ	Name	Bedeutung

T	min()	der positive, normalisierte
Minimalwert

int	digits	die Anzahl der Stellen in der
Mantisse

int	radix	die Basis (oder Radix) der
Exponentendarstellung

T	epsilon()	die Differenz zwischen 1 und dem
am kleinsten darstellbaren
Wert größer 1

T	round_error()	Maß des Rundungsfehlers

int	min_exponent	minimaler negativer Exponent

int	min_exponent10	Minimalwert, wenn 10 in diese
Potenz erhoben im Bereich liegt

int	max_exponent	maximaler positiver Exponent

int	max_exponent10	Maximalwert, wenn 10 in diese
Potenz erhoben im Bereich liegt

bool	has_infinity	true, wenn der Typ eine
Darstellung positiver Unendlichkeit
besitzt

T	infinity()	Darstellung von Unendlichkeit,
wenn verfügbar

bool	has_quiet_NaN	true, wenn eine Darstellung einer
stummen Nachricht "Not a
Number" vorhanden ist

T	quiet_NaN()	Darstellung einer stummen NaN,
wenn vorhanden

bool	has_signaling_NaN	true, wenn eine Darstellung einer
Signalnachricht NaN
vorhanden ist

T	signaling_NaN()	Darstellung einer Signal- NaN,
wenn vorhanden

bool	has_denorm	true, wenn die Darstellung
denormalisierte Werte erlaubt

T	denorm_min()	positiver denormalisierter
Minimalwert

bool	is_iec559	true, wenn die Darstellung dem
Standard IEC 559 entspricht

bool	tinyness_before	true, wenn Kleinheit vor dem
Runden entdeckt wird

round_style	Rundungsstil für den Typ

Für den Datentyp float ist der Wert im Feld radix, das die Basis der Exponentialdarstellung repräsentiert, äquivalent mit der symbolischen Konstante FLT_RADIX.

Für die Typen float, double und long double ist der Wert von epsilon auch als FLT_EPSILON, DBL_EPSILON und LDBL_EPSILON verfügbar.

NaN bedeutet "Not a Number" (keine Zahl). Es handelt sich um einen darstellbaren Wert, der dennoch keiner numerischen Größe entspricht. Viele numerische Algorithmen manipulieren solche Werte.

Der Standard IEC 559 ist ein Standard, den die International Electrotechnical Commission anerkennt. Er ist gleich dem Standard IEEE 754.

Der Wert, den die Funktion round_style() zurückgibt, ist einer der folgenden: round_indeterminate, round_toward_zero, round_to_nearest, round_toward_infinity oder round_toward_neg_infinity.


Glossar

Ausgabeiterator Ein Iterator, der nur verwendet werden kann, um Elemente in einen Container zu schreiben, nicht aber, um sie zu lesen.

Bereich Teilmenge der in einem Container enthaltenen Elemente. Ein Bereich wird typischerweise durch zwei Iteratoren bezeichnet.

Bereichsendeiterator Ein Iterator, der das Ende eines Wertebereichs markiert, wie etwa das Ende der Menge der Werte, die von einem Container gehalten werden.

bidirektionaler Iterator Ein Iterator, der zum Lesen und Schreiben verwendet wird, und der sowohl vorwärtsschreitend als auch rückwärtsschreitend eingesetzt werden kann.

binäre Funktion Eine Funktion, die zwei Argumente benötigt.

Binder Ein Funktionsadapter, der für die Umwandlung eines binären Funktionsobjekts (mit zwei Argumenten) in ein unäres Funktionsobjekt (mit einem Argument) verwendet wird, indem einer der Argumentwerte an eine spezifische Konstante gebunden wird.

Container-Klasse Eine Klasse, die verwendet wird, um eine Kollektion von Werten ähnlichen Typs zu halten. Zu den in der Standardbibliothek zur Verfügung stehenden Container-Klassen gehören Vector, List, Deque, Set, Map, Stack, Queue und priority_queue.

Deque Eine indizierbare Container-Klasse. Auf Elemente kann über ihre Position im Container zugegriffen werden. Dadurch steht schneller, direkter Zugriff auf Elemente zur Verfügung. Einfügevorgänge sind sowohl am Anfang als auch am Ende eines Deques effizient. Einfügungen in der Mitte sind nicht effizient.

Einfügeiterator Ein Adapter zum Umwandeln von Iterator-Schreiboperationen in Einfügevorgänge in einen Container.

Eingabeiterator Ein Iterator, der zum Lesen von Werten in einer Sequenz verwendet werden kann, nicht aber zum Schreiben.

Funktionsobjekt Eine Instanz einer Klasse, die den Klammeroperator als eine ihrer Elementfunktionen definiert. Wenn ein Funktionsobjekt anstelle einer Funktion verwendet wird, wird die Klammer-Elementfunktion dann ausgeführt, wenn normalerweise die Funktion aufgerufen würde.

Generator Eine Funktion, die potentiell bei jedem Aufruf einen anderen Wert zurückliefern kann. Ein Zufallszahlengenerator ist ein Beispiel.

generischer Algorithmus Ein Algorithmen-Template, das nicht auf einen bestimmten Container-Typ spezialisiert ist. Aufgrund dieser Tatsache können generische Algorithmen mit einer großen Bandbreite verschiedener Containerarten eingesetzt werden.

geordnete Kollektion Eine Kollektion, in der alle Werte nach einem binären Vergleichsoperator geordnet sind. Der Datentyp Set erhält automatisch eine geordnete Kollektion aufrecht. Andere Kollektionen (Vector, Deque, List) lassen sich in geordnete Kollektionen überführen.

Heap Eine Organisationsform für Kollektionen, die schnelles Einfügen neuer Werte erlaubt sowie schnellen Zugriff und schnelles Entfernen für den größten Wert der Kollektion.

heterogene Kollektion Eine Kollektion von Werten unterschiedlichen Typs. In der Standardbibliothek kann eine heterogene Kollektion nur durch das Speichern von Zeigern auf Objekte anstelle der Objekte selbst realisiert werden.

Iterator Eine Verallgemeinerung der Idee eines Zeigers. Ein Iterator bezeichnet ein bestimmtes Element in einem Container und kann verwendet werden, um der Reihe nach auf die in einem Container enthaltenen Elemente zuzugreifen.

konstanter Iterator Ein Iterator, der nur für das Lesen von Werten verwendet werden kann, nicht zum Ändern der Werte in einer Sequenz.

list Eine lineare Container-Klasse. Elemente werden in einer Folge gehalten. Bietet nur für das erste und das letzte Element schnellen Zugriff, Einfügevorgänge sind jedoch auch in der Mitte einer Liste effizient.

map Eine indizierte und geordnete Container-Klasse. Im Gegensatz zu einem Vector oder einem Deque eignet sich jeder geordnete Datentyp (wie ein String oder ein Zeichen) als Indexwert für eine Map. Werte werden in einer Folge gehalten und lassen sich effizient einfügen, entfernen oder zugreifen, und zwar in beliebiger Reihenfolge.

multimap Eine Sonderform von Map, die die Indizierung mehrerer Elemente durch den gleichen Indexwert gestattet.

multiset Eine Sonderform von Set, bei der mehrere Exemplare des gleichen Werts in einer Kollektion enthalten sein können.

Negator Ein Adapter, der ein Prädikat-Funktionsobjekt umwandelt, und ein neues Funktionsobjekt erzeugt, das beim Aufruf den umgekehrten Wert ergibt.

Prädikat Eine Funktion oder ein Funktionsobjekt, das bei Aufruf einen booleschen Wert (true/false) oder einen Integerwert zurückgibt.

Prädikatfunktion Ein Prädikat.

priority_queue Eine Adapter-Container-Klasse, die normalerweise auf einem Vector oder einem Deque aufbaut. Die Priority Queue ist für den schnellen Zugriff auf das größte Element in der Kollektion und dessen schnelles Entfernen ausgelegt.

Queue Eine Adapter-Container-Klasse, die normalerweise auf einer Liste oder einem Deque aufbaut. Die Queue bietet den schnellen Zugriff auf das oberste Element. Elemente werden aus einer Queue in der Reihenfolge ihrer Einfügung entfernt.

Random Access-Iterator Ein Iterator, der indiziert werden kann, so daß er den Zugriff auf die Elemente eines Containers in beliebiger Reihenfolge gestattet.

Reverse-Iterator Ein Iterator, der sich in umgekehrter Reihenfolge über eine Sequenz von Werten bewegt, beispielsweise von hinten nach vorn.

Sequenz Ein Teil oder die Gesamtheit der in einem Container enthaltenen Elemente. Eine Sequenz wird normalerweise durch einen Bereich bezeichnet.

set Eine geordnete Container-Klasse. Der Containertyp Set ist für Einfüge- und Entfernungsvorgänge und für Tests auf enthaltene Elemente optimiert.

stack Eine Adapter-Container-Klasse, die normalerweise auf einem Vector oder einem Deque aufbaut. Der Stack bietet schnellen Zugriff auf das oberste Element. Elemente werden von einem Stack in der umgekehrten Reihenfolge ihrer Einfügung entfernt.

Stream-Iterator Ein Adapter, der Iteratoroperationen in Stream-Operationen umwandelt. Kann zum Lesen aus oder Schreiben in einen E/A-Stream verwendet werden.

unäre Funktion Eine Funktion, die nur ein Argument annimmt. Das Anwenden eines Binders auf eine binäre Funktion ergibt eine unäre Funktion.

vector Eine indizierbare Container-Klasse. Auf Elemente wird über einen Index, der ihre Position im Container angibt, zugegriffen. Bietet schnellen direkten Zugriff auf Elemente. Einfügungen am Ende eines Vectors sind effizient. Einfügungen in der Mitte sind hingegen nicht effizient.

Vorwärtsiterator Ein Iterator, der sowohl zum Lesen als auch zum Schreiben eingesetzt werden kann, der aber nur vorwärtsschreitend ist.

wide String String mit 16-Bit-Zeichen. Wide Strings sind für viele nicht lateinische Alphabete, wie z.B. das japanische, erforderlich.


Klassenreferenz der Standard C++ Bibliothek

Dieser Referenzteil enthält eine alphabetische Liste aller Klassen, Algorithmen und Funktionsobjekte, die diese Version der Standardbibliothek von Rouge Wave zur Verfügung stellt. Der graue Balken auf der ersten Seite jedes Eintrags gibt die Kategorie an (zum Beispiel Algorithmen, Container, etc.), zu der die Klasse gehört. Die Tabellen auf den nächsten Seiten enthalten eine nach Kategorien geordnete Liste der Klassen.

Der Referenzteil für jede Klasse beginnt mit einer kurzen Zusammenfassung der Klasse und einer Zusammenstellung der Header-Datei(en), einer Deklaration und Definition eines Klassenobjekts sowie den Typdefinitionen für die Klasse. Der Referenzteil enthält außerdem eine Beschreibung und fast immer ein Beispiel. Alle Methoden, die eine Klasse verwendet - einschließlich Konstruktoren, Operatoren, Elementfunktionen usw. - werden in Kategorien nach ihrer allgemeinen Verwendung gruppiert und beschrieben. Die Kategorien sind kein Bestandteil der Sprache C++, sie bieten nur ein Verfahren zum Organisieren der Methoden.

In dieser Dokumentation finden sich häufig Bezüge auf "self", die "*this" bedeuten.


Konformität mit Standards

Die in diesem Referenzteil vorgestellten Informationen entsprechen den Anforderungen des ANSI X3J16/ISO WG21 Joint C++ Committee.

Algorithmen

#include <algorithm>	adjacent_find

binary_search

copy

copy_backward

count

count_if

equal

equal_range

fill

fill_n

find

find_first_of

find_if

for_each

generate

generate_n

includes

inplace_merge

iter_swap

lexicographical_compare

lower_bound

make_heap

max

max_element

merge

min

min_element

mismatch

next_permutation

nth_element

partial_sort

partial_sort_copy

partition

pop_heap

prev_permutation

push_heap

random_shuffle

remove

remove_copy

remove_copy_if

remove_if

replace

replace_copy

replace_copy_if

replace_if

reverse

reverse_copy

rotate

rotate_copy

search

set_difference

set_intersection

set_symmetric_difference

set_union

sort

sort_heap

stable_partition

stable_sort

swap

swap_ranges

transform

unique

unique_copy

upper_bound

Bibliothek der komplexen Zahlen

#include <complex>	complex

Container

#include <deque>	deque

#include <list>	list

#include <map> für map und multimap	map

multimap

multiset

#include <queue> für queue und priority_queue	priority_queue

queue

#include <set> für set und multiset	set

#include <stack>	stack

#include <vector>	vector

Funktionsadapter

#include <functional>	bind1st

bind2nd

not1

not2

ptr_fun

Funktionsobjekte

#include <functional>	binary_function

binary_negate

binder1st

binder2nd

divides

equal_to

greater

greater_equal

less

less_equal

logical_and

logical_not

logical_or

minus

modulus

negate

not_equal_to

plus

pointer_to_binary-function

pointer_to_unary_function

times

unary_function

Generalisierte numerische Operationen

#include <numeric>	accumulate

adjacent_difference

accumulate

inner_product

partial_sum

Einfügeiteratoren

#include <iterator>	back_insert_iterator

back_inserter

front_insert_iterator

front_inserter

insert_iterato

inserter

Iteratoren

#include <iterator>	bidirektionaler Iterator

Vorwärtsiterator

Eingabeiterator

Ausgabeiterator

Random Access-Iterator

reverse_bidirectional_iterator

reverse_iterator

Iteratoroperationen

#include <iterator>	advance

distance

Primitive zur Speicherverwaltung

#include <memory>	allocate

construct

deallocate

destroy

get_temporary_buffer

return_temporary_buffer

Speicherverwaltung

#include <memory>	raw_storage_iterator

uninitialized_copy

uninitialized_fill

uninitialized_fill_n

Bibliothek der numerischen Grenzen

#include <limits>	numeric limits

String-Bibliothek

#include <string>	basic_string

string

wstring

Hilfsklassen

#include <utility>	pair

Hilfsoperatoren

#include <utility>	operator!=

operator>

operator<=

operator>=


accumulate

Generalisierte numerische Operation

Akkumuliert alle Elemente innerhalb eines Bereichs zu einem einzigen Wert.

Syntax

#include <numeric>

template <class InputIterator, class T>

T accumulate (InputIterator first,

              InputIterator last,

              T init);

template <class InputIterator,

          class T,

          class BinaryOperation>

T accumulate (InputIterator first,

              InputIterator last,

              T init,

              BinaryOperation binary_op);

Beschreibung

Dieser Algorithmus akkumuliert oder "summiert" alle Elemente im Bereich [first, last) zu einem einzigen Wert. Wenn Sie beispielsweise accumulate auf die Sequenz {1, 2, 3} anwenden, ist das Ergebnis {6}.

Die erste Version des Algorithmus verwendet Plus (+) als Standardoperator. Mit der zweiten Version können Sie eine beliebige binäre Operation angeben.

Damit nicht das Problem auftritt, leere Bereiche zu akkumulieren, verlangt accumulate einen Anfangswert, init, der als erstes Element der Akkumulation verwendet wird. Normalerweise ist init der Identitätswert der binären Operation von accumulate. (Zum Beispiel ist in der Standardversion des Algorithmus init normalerweise gleich 0. Wenn Sie festlegen, daß binary_op Multiplikation bedeutet, ist init normalerweise gleich 1.)

Die Akkumulation wird durchgeführt, indem der Akkumulator acc mit dem Anfangswert init initialisiert und anschließend mit acc = acc + *i oder acc = binary_op(acc, *i) für jeden Iterator i im Bereich in der Reihenfolge [first, last) modifiziert wird. Wenn die Sequenz leer ist, gibt accumulate init zurück.

accumulate führt genau last-first Anwendungen der binären Operation aus.

Beispiel

 #include <numeric>   //für accumulate

 #include <vector>    //für vector

 #include <functional> //für times

 using namespace std;

 int main()

 {

   //Typdefinition für Vektoriteratoren

   typedef vector<int>::iterator iterator;

   //Initialisieren eines Vektors mit einem Array von Integerwerten

   int d1[10] = {1,2,3,4,5,6,7,8,9,10};

   vector<int> v1(d1, d1+10);

   //Akkumulieren der Summen und Produkte

   int sum = accumulate(v1.begin(), v1.end(), 0);

   int prod = accumulate(v1.begin(), v1.end(),

               1, times<int>());

   //Ausgabe der Ergebnisse

   cout << "For the series: ";

   for(iterator i = v1.begin(); i != v1.end(); i++)

  cout << *i << " ";

   cout << " where N = 10." << endl;

   cout << "The sum = (N*N + N)/2 = " << sum << endl;

   cout << "The product = N! = " << prod << endl;

   return 0;

 }


adjacent_difference

Generalisierte numerische Operation

Gibt eine Sequenz der Unterschiede zwischen jedem benachbarten Elementpaar in einem Bereich aus.

Syntax

#include <numeric>

template <class InputIterator, class OutputIterator>

OutputIterator adjacent_difference (InputIterator first,

                                    InputIterator last,

                                    OutputIterator result);

template <class InputIterator,

          class OutputIterator,

          class BinaryOperation>

OutputIterator adjacent_difference (InputIterator first,

                                    InputIterator last,

                                    OutputIterator result,

                                    BinaryOperation bin_op);

Beschreibung

adjacent_difference füllt gewissermaßen eine Sequenz mit der Differenz zwischen aufeinanderfolgenden Elementen in einem Container. Das Ergebnis ist eine Sequenz, in der das erste Element gleich dem ersten Element der verarbeiteten Sequenz ist. Die restlichen Elemente sind gleich den berechneten Differenzen zwischen den benachbarten Elementen. Wenn Sie beispielsweise adjacent_difference auf {1,2,3,5} anwenden, ist das Ergebnis {1,1,1,2}.

Per Voreinstellung wird die Differenz mittels Subtraktion ermittelt. Sie können jedoch jeden beliebigen binären Operator verwenden. Der binäre Operator wird anschließend auf die benachbarten Elemente angewendet. Wenn Sie beispielsweise den Operator Plus (+) verwenden, ist das Ergebnis der Anwendung von adjacent_difference auf {1,2,3,5} die Sequenz {1,3,5,8}.

Formal weist adjacent_difference jedem Element, auf das sich der Iterator i im Bereich [result + 1, result + (last - first)) bezieht, einen Wert zu, der gleich dem passenden der folgenden Werte ist:

*(first  + (i - result)) - *(first + (i - result) - 1)

oder

binary_op (*(first + (i - result)), *(first + (i - result) - 1))

result wird der Wert von *first zugewiesen.

Der Iterator, den adjacent_difference zurückgibt, ist gleich result + (last - first).

result kann gleich first sein. Hierdurch können Sie das Ergebnis der Anwendung von adjacent_difference in die Originalsequenz einfügen.

Dieser Algorithmus führt genau (last-first) - 1 Anwendungen der Standardoperation (-) oder binary_op aus.

Beispiel

 #include<numeric>       //Für adjacent_difference

 #include<vector>        //Für vector

 #include<functional>    //Für times

 using namespace std;

 int main()

 {

   //Initialisieren eines Vektors aus Integerwerten aus einem Array

   int arr[10] = {1,1,2,3,5,8,13,21,34,55};

   vector<int> v(arr,arr+10);

   //Zwei nicht initialisierte Vektoren zum Speichern der Ergebnisse

   vector<int> diffs(10), prods(10);

   //Berechnen der Differenz mit dem Standardoperator (Minus)

   adjacent_difference(v.begin(),v.end(),diffs.begin());

   //Berechnen der Differenz mit dem Operator times

   adjacent_difference(v.begin(), v.end(), prods.begin(),

         times<int>());

   //Ausgabe der Ergebnisse

   cout << "For the vector: " << endl << "     ";

   copy(v.begin(),v.end(),ostream_iterator<int>(cout," "));

   cout << endl << endl;

   cout << "The differences between adjacent elements are: "

        << endl << "     ";

   copy(diffs.begin(),diffs.end(),

        ostream_iterator<int>(cout," "));

   cout << endl << endl;

   cout << "The products of adjacent elements are: "

        << endl << "     ";

   copy(prods.begin(),prods.end(),

        ostream_iterator<int>(cout," "));

   cout << endl;

   return 0;

 }


adjacent_find

Algorithmus

Sucht das erste benachbarte Elementpaar in einer Sequenz, das äquivalent ist.

Syntax

#include <algorithm>

template <class ForwardIterator>

  ForwardIterator

  adjacent_find(ForwardIterator first, ForwardIterator last);

template <class ForwardIterator, class Predicate>

 ForwardIterator

  adjacent_find(ForwardIterator first, ForwardIterator last,

                Predicate pred);

Beschreibung

Es gibt zwei Versionen des Algorithmus adjacent_find. Die erste Version sucht gleiche benachbarte Elemente in einer Sequenz, die durch die Iteratoren first und last definiert ist, und gibt einen Iterator i zurück, der auf das erste der gleichen Elemente zeigt. Mit der zweiten Version können Sie Ihre eigene binäre Funktion angeben, um auf eine Bedingung zu testen. Sie liefert einen Iterator i zurück, der auf das erste der Elementpaare zeigt, das den Bedingungen der binären Funktion genügt. Anders gesagt gibt adjacent_find den ersten Iterator i zurück, bei dem sowohl i als auch i + 1 im Bereich [first, last) liegen und eine der folgenden Bedingungen erfüllt ist:

  *i  ==  *(i  +  1)

oder

pred(*i,*(i  +  1))  == true

Wenn adjacent_find keine Entsprechung findet, wird last zurückgegeben.

adjacent_find führt genau find(first,last,value) - first Anwendungen des entsprechenden Prädikats durch.

Beispiel

#include <vector>

 #include <algorithm>

 using namespace std;

 int main()

 {

   typedef vector<int>::iterator iterator;

   int d1[10] = {0,1,2,2,3,4,2,2,6,7};

   // Erzeugen eines Vektors

   vector<int> v1(d1,d1 + 10);

   // Versuche find

   iterator it1 = find(v1.begin(),v1.end(),3);

   // it1 = v1.begin() + 4;

   // Versuche find_if

   iterator it2 =

      find_if(v1.begin(),v1.end(),bind1st(equal_to<int>(),3));

   // it2 = v1.begin() + 4

   // Versuche beide Varianten adjacent_find

   iterator it3 = adjacent_find(v1.begin(),v1.end());

   // it3 = v1.begin() +2

   iterator it4 =

      adjacent_find(v1.begin(),v1.end(),equal_to<int>());

   // v4 = v1.begin() + 2

   // Ausgabe der Ergebnisse

   cout << *it1 << " " << *it2 << " " << *it3 << " "

        << *it4 << endl;

   return 0;

 }


advance

Iteratoroperation

Bewegt einen Iterator um einen bestimmten Abstand vorwärts oder rückwärts (wenn möglich).

Syntax

#include <iterator>

template <class InputIterator, class Distance>

void advance (InputIterator& i, Distance n);

Beschreibung

Mit der Template-Funktion advance kann ein Iterator um eine beliebige Entfernung durch einen Container bewegt werden. Bei bidirektionalen und Random Access-Iteratoren kann diese Entfernung negativ sein. Diese Funktion verwendet den Operator + und den Operator - für Random Access-Iteratoren. Diese Implementierung bietet eine konstante Ausführungszeit. Bei Eingabe-, Vorwärts- und bidirektionalen Iteratoren verwendet advance den Operator ++ und bietet damit lineare Ausführungszeit. advance verwendet für lineare Ausführungszeit bei negativen Entfernungen auch den Operator -- für bidirektionalen Iteratoren.

Wenn n positiv ist, inkrementiert advance die Iteratorenreferenz i um n. Bei negativem n dekrementiert advance die Referenz i. Beachten Sie, daß advance ein negatives Argument n nur für Random Access- und bidirektionale Iteratoren akzeptiert.

Beispiel

 #include<iterator>

 #include<list>

 using namespace std;

 int main()

 {

   //Initialisieren einer Liste mit einem Array

   int arr[6] = {3,4,5,6,7,8};

   list<int> l(arr,arr+6);

   //Deklarieren eines Listeniterators, beispielsweise eines ForwardIterators

   list<int>::iterator itr = l.begin();

   //Ausgabe der Originalliste

   cout << "For the list: ";

   copy(l.begin(),l.end(),ostream_iterator<int>(cout," "));

   cout << endl << endl;

   cout << "When the iterator is initialized to l.begin(),"

        << endl << "it points to " << *itr << endl << endl;

   // operator+ steht für einen ForwardIterator nicht zur Verfügung,

   // also advance verwenden.

   advance(itr, 4);

   cout << "After advance(itr,4), the iterator points to "

        << *itr << endl;

   return 0;

 }


Algorithmen

Generische Algorithmen zur Durchführung verschiedener Operationen mit Containern und Sequenzen.

Syntax

Die Zusammenfassung jedes Algorithmus finden Sie unter seinem Eintrag im Referenzhandbuch.

Beschreibung

Die C++ Standardbibliothek bietet ein sehr flexibles Framework für die Verwendung generischer Algorithmen mit Containern. Die Bibliothek stellt außerdem eine große Auswahl dieser Algorithmen zum Suchen, Sortieren, Zusammenführen, Umwandeln, Durchsuchen und für viele andere Aufgaben zur Verfügung.

Jeder Algorithmus kann auf eine Vielzahl von Containern angewendet werden, auch auf solche, die von einem Benutzer der Bibliothek definiert werden. Die folgenden Merkmale kennzeichnen Algorithmen als generisch:

Generische Algorithmen greifen auf die Kollektion über Iteratoren zu.

Algorithmen sind gemäß ihren Iteratorentypen als Templates implementiert.

Jeder Algorithmus ist so ausgelegt, daß er die kleinstmögliche Zahl von Diensten von dem Iterator benötigt, den er verwendet.

Zusätzlich zu bestimmten benötigten Iteratorenfähigkeiten verlangen einige Algorithmen, daß sich ein Container in einem bestimmten Zustand befindet. Einige Algorithmen können z.B. nur mit zuvor sortierten Containern arbeiten.

Es gibt verschiedene Methoden, um Algorithmen in Gruppen zusammenzufassen. Die allgemeinste Unterscheidung gruppiert Algorithmen danach, ob sie die Elemente der Sequenz verändern. Die Algorithmen, die den Inhalt eines Containers ändern (oder mutieren) bilden die Änderungsgruppe. Alle anderen Algorithmen verändern nichts. Algorithmen können auch nach dem Operationstyp, den sie durchführen, gruppiert werden. Schließlich können sie auch nach dem Iteratorentyp, den sie benötigen, unterschieden werden, da die meisten Algorithmen über Iteratoren auf Daten zugreifen. Die nächsten drei Abschnitte enthalten nach diesen Kriterien zusammengestellte Listen der Algorithmen.

Algorithmen mit verändernder/nicht verändernder Funktion

Die allgemeinste Unterscheidung gruppiert Algorithmen in zwei Haupttypen: verändernde und nicht verändernde Algorithmen. Die Algorithmen, die den Inhalt eines Containers ändern (oder mutieren), fallen in die Änderungsgruppe. Alle anderen gelten als nicht verändernde Algorithmen. Beispielsweise sind sowohl fill als auch sort Änderungsalgorithmen, während find und for_each die Sequenz nicht verändern.

Nicht verändernde Operationen

accumulate       find                      max

adjacent_find    find_if                   max_element

binary_search    find_first_of             min

count            for_each                  min_element

count_if         includes                  mismatch

equal            lexicographical_compare   nth_element

equal_range      lower_bound mismatch      search

Änderungsoperationen

copy                           remove_if

copy_backward                  replace

fill                           replace_copy

fill_n                         replace_copy_if

generate                       replace_if

generate_n                     reverse

inplace_merge                  reverse_copy

iter_swap                      rotate swap

make_heap                      rotate_copy

merge                          set_difference

nth_element                    set_symmetric_difference

next_permutation               set_intersection

partial_sort                   set_union

partial_sort_copy              sort

partition                      sort_heap

prev_permutation               stable_partition

push_heap                      stable_sort

pop_heap                       swap

random_shuffle                 swap_ranges

remove                         transform

remove_copy                    unique

remove_copy_if                 unique_copy

Beachten sie, daß die Bibliothek für viele Algorithmen sowohl stationäre Versionen als auch Kopierversionen zur Verfügung stellt, zum Beispiel replace und replace_copy. Die Bibliothek bietet außerdem Versionen der Algorithmen, die die Verwendung von Standardvergleichsoperatoren oder Vergleichsoperatoren, die der Benutzer zur Verfügung stellt, ermöglichen. Diese Funktionen werden häufig überladen. In einigen Fällen (in denen Überladen unpraktisch oder unmöglich ist) unterscheiden sich jedoch die Namen (z.B. replace, das Gleichheit als Kriterium zum Ersetzen verwendet und replace_if, das auf eine vom Benutzer zur Verfügung gestellte Vergleichsfunktion zugreift).

Algorithmen nach Operationen gruppiert

Algorithmen können außerdem nach der Art der Operationen unterschieden werden, die sie ausführen. Im folgenden finden Sie alle Algorithmen nach ähnlichen Operationen gruppiert.

Initialisierungsoperationen

fill                           generate

fill_n                         generate_n

Suchoperationen

adjacent_find                  find_if

count                          find_first_of

count_if                       search

find

Binäre Suchoperationen (Elemente müssen sortiert sein)

binary_search                  lower_bound

equal_range                    upper_bound

Vergleichsoperationen

equal                          mismatch

lexicographical_compare

Kopieroperationen

copy                           copy_backward

Umwandlungsoperationen

partition                      reverse

random_shuffle                 reverse_copy

replace                        rotate

replace_copy                   rotate_copy

replace_copy_if                stable_partition

replace_if                     transform

Vertauschoperationen

swap                           swap_ranges

Durchsuchoperationen

accumulate                     for_each

Entfernungsoperationen

remove                         remove_if

remove_copy                    unique

remove_copy_if                 unique_copy

Sortieroperationen

nth_element                    sort

partial_sort                   stable_sort

partial_sort_copy

Zusammenführungsoperationen (Elemente müssen sortiert sein)

inplace_merge                  merge

Set-Operationen (Elemente müssen sortiert sein)

includes                       set_symmetric_difference

set_difference                 set_union

set_intersection

Heap-Operationen

make_heap                      push_heap

pop_heap                       sort_heap

Minimum und Maximum

max                            min

max_element                    min_element

Permutationsgeneratoren

next_permutation               prev_permutation

Algorithmen nach Iteratorkategorie gruppiert

Jeder Algorithmus benötigt eine bestimmte Art von Iteratoren (eine Beschreibung der Iteratoren und ihrer Funktionen finden Sie unter dem Eintrag iterator in dieser Dokumentation). Die folgenden Listen gruppieren die Algorithmen nach dem Iteratorentyp, den sie benötigen.

Algorithmen, die keine Iteratoren verwenden

max                    min                 swap

Algorithmen, die nur input_iteratoren benötigen

accumulate             find                mismatch

count                  find_if

count_if               includes

equal                  inner_product

for_each               lexicographical_compare

Algorithmen, die nur output_iteratoren benötigen

fill_n                 generate_n

Algorithmen, die aus input_iteratoren lesen und in output_iteratoren schreiben

adjacent_difference    replace_copy        transform

copy                   replace_copy_if     unique_copy

merge                  set_difference

partial_sum            set_intersedtion

remove_copy            set_symmetric_difference

remove_copy_if         set_union

Algorithmen, die Vorwärtsiteratoren benötigen

adjacent_find          lower_bound         rotate

binary_search          max_element         search

equal_range            min_element         swap_ranges

fill                   remove              unique

find_first_of          remove_if           upper_bound

generate               replace

iter_swap              replace_if

Algorithmen, die aus forward_iteratoren lesen und in output_iteratoren schreiben

rotate_copy

Algorithmen, die bidirectional_iteratoren benötigen

copy_backward          partition

inplace_merge          prev_permutation

next_permutation       reverse

                       stable_permutation

Algorithmen, die aus bidirectional_iteratoren lesen und in output_iteratoren schreiben

reverse_copy

Algorithmen, die Random Access-Iteratoren benötigen

make_heap              pop_heap            sort

nth_element            push_heap           sort_heap

partial_sort           random_shuffle      stable_sort

Algorithmen, die aus input_iteratoren lesen und in random_access_iteratoren schreiben

partial_sort_copy


allocate

Siehe auch	Primitiv zur Speicherverwaltung

Zeigerbasiertes Primitiv zur Speicherverwaltung.

Syntax

#include <memory>

template <class T>

 T* allocate (ptrdiff_t n, T*);

Beschreibung

allocate reserviert einen nicht initialisierten Speicherpuffer der Größe n* sizeof(T) im Systemspeicher und gibt einen typisierten Zeiger an diesen Puffer zurück.


Assoziative Container

Assoziative Container sind geordnete Container. Diese Container stellen Elementfunktionen zur Verfügung, die effizientes Einfügen, Ermitteln und Manipulieren von Indizes ermöglichen. Die Standardbibliothek enthält die assoziativen Container map, multimap, set und multiset. map und multimap verbinden Werte mit den Indizes und ermöglichen so ein schnelles Ermitteln des Werts durch schnelles Finden des Index. set und multiset speichern nur Indizes und erlauben so das schnelle Ermitteln des Index selbst.


auto_ptr

Speicherverwaltung

Eine einfache, intelligente Zeigerklasse.

Syntax

 #include <memory>

 template <class X> class auto_ptr {

  public:

   // Konstruktor/Kopieren/Entfernen

   explicit auto_ptr (X* p = 0);

   auto_ptr (auto_ptr<X>&);

   void operator= (auto_ptr<X>&);

   ~auto_ptr ();

   // Elemente

   X& operator* () const;

   X* operator-> () const;

   X* get () const;

   X* release ();

   void reset (X* p = 0);

 };

Beschreibung

Die Template-Klasse auto_ptr ist mit einem über new erhaltenen Zeiger verknüpft und löscht dieses Objekt, wenn das Objekt auto_ptr selbst entfernt wird (z.B. wenn der Gültigkeitsbereich des Blocks verlassen wird). auto_ptr kann für Exception-sichere Aufrufe des Operators new verwendet werden. Die Semantik der Klasse auto_ptr verlangt strenge Besitzverhältnisse: auf ein Objekt kann sicher nur mit einem auto_ptr gezeigt werden. Beim Kopieren eines auto_ptrs wird daher der Zeiger kopiert und der Besitz an das Ziel übertragen.

Konstruktor

explicit

auto_ptr (X* p = 0);

Erzeugt ein Objekt der Klasse auto_ptr<X> und initialisiert den verknüpften Zeiger mit p. Es ist erforderlich, daß p auf ein Objekt der Klasse X oder auf eine von X abgeleitete Klasse zeigt, für die delete p definiert und zugreifbar ist, oder p muß ein Nullzeiger sein.

auto_ptr (auto_ptr<X>& a);

Erzeugt ein Objekt der Klasse auto_ptr<X> und kopiert das Argument a nach *this. *this wird der neue Besitzer des zugrundeliegenden Zeigers.

Destruktor

~auto_ptr ();

Löscht den zugrundeliegenden Zeiger.

Operatoren

void

operator= (auto_ptr<X>& a);

Zuweisungsoperator. Kopiert das Argument a nach *this. *this wird der neue Besitzer des zugrundeliegenden Zeigers. Wenn *this bereits einen Zeiger besitzt, wird dieser Zeiger zuerst gelöscht.

X&

operator* () const;

Gibt eine Referenz auf das Objekt zurück, auf das der zugrundeliegende Zeiger zeigt.

X*

operator-> () const;

Gibt den zugrundeliegenden Zeiger zurück.

Elementfunktionen

X*

get () const;

Gibt den zugrundeliegenden Zeiger zurück.

X*

release();

Gibt den Besitz des zugrundeliegenden Zeigers frei. Liefert diesen Zeiger zurück.

void

reset (X* p = 0);

Es ist erforderlich, daß p auf ein Objekt der Klasse X oder auf eine von X abgeleitete Klasse zeigt, für die delete p definiert und zugreifbar ist, oder p muß ein Nullzeiger sein. Löscht den aktuellen zugrundeliegenden Zeiger und setzt ihn anschließend auf p zurück.

Beispiel

  //

  #include <iostream.h>

  #include <memory>

 using namespace std;

  //

  // Eine einfache Struktur.

  //

  struct X

  {

    X (int i = 0) : m_i(i) { }

    int get() const { return m_i; }

    int m_i;

  };

  int main ()

  {

   //

   // b enthält einen Zeiger auf ein X.

   //

   auto_ptr<X> b(new X(12345));

   //

   // a ist jetzt der Besitzer des zugrundeliegenden Zeigers.

   //

   auto_ptr<X> a = b;

   //

   // Ausgabe des Werts, den der zugrundeliegende Zeiger enthält.

   //

   cout << a->get() << endl;

   //

   // Der Zeiger wird gelöscht, wenn a beim Verlassen des
   // Gültigkeitsbereichs entfernt wird.

//

   return 0;

  }


back_insert_iterator, back_inserter

Einfügeiterator

Ein Einfügeiterator, der verwendet wird, um Elemente am Ende einer Kollektion einzufügen.

Syntax

#include <iterator>

template <class Container>

 class back_insert_iterator : public output_iterator {

protected:

   Container& container;

public:

   back_insert_iterator (Container& x);

   back_insert_iterator<Container>&

   operator= (const Container::value_type& value);

   back_insert_iterator<Container>& operator* ();

   back_insert_iterator<Container>& operator++ ();

   back_insert_iterator<Container> operator++ (int);

};

template <class Container>

 back_insert_iterator<Container> back_inserter (Container& x)

Beschreibung

Mit Einfügeiteratoren können Sie neue Elemente in eine Kollektion einfügen, anstatt den Wert eines neuen Elements über den Wert eines vorhandenen Elements zu kopieren. Die Klasse back_insert_iterator wird verwendet, um Elemente am Ende einer Kollektion einzufügen. Die Funktion back_inserter erzeugt eine Instanz eines back_insert_iterators für einen bestimmten Kollektionstyp. Ein back_insert_iterator kann mit vectoren, deques und listen verwendet werden, nicht jedoch mit maps oder sets.

Beispiel

/*****************************************************************

 *

 * ins_itr.cpp - Beispielprogramm für einen Einfügeiterator.

 *

 * $Id: ins_itr.cpp,v 1.7 1995/10/06 18:18:03 hart Exp $

 *

 * $$RW_INSERT_HEADER "slyrs.str"

 *

 ****************************************************************/

 #include <iterator>

 #include <deque>

 using namespace std;

 int main ()

 {

  //

  // Initialisieren eines Deques mit einem Array.

  //

  int arr[4] = { 3,4,7,8 };

  deque<int> d(arr+0, arr+4);

  //

  // Ausgabe des Original-Deques.

  //

  cout << "Start with a deque: " << endl << "   ";

  copy(d.begin(), d.end(), ostream_iterator<int>(cout," "));

  //

  // In der Mitte einfügen.

  //

  insert_iterator<deque<int> > ins(d, d.begin()+2);

  *ins = 5; *ins = 6;

  //

  // Ausgabe des neuen Deques.

  //

  cout << endl << endl;

  cout << "Use an insert_iterator: " << endl << "   ";

  copy(d.begin(), d.end(), ostream_iterator<int>(cout," "));

  //

  // Ein Deque aus vier Einsen.

  //

  deque<int> d2(4, 1);

  //

  // d2 vor d einfügen.

  //

  copy(d2.begin(), d2.end(), front_inserter(d));

  //

  // Ausgabe des neuen Deques.

  //

  cout << endl << endl;

  cout << "Use a front_inserter: " << endl << "   ";

  copy(d.begin(), d.end(), ostream_iterator<int>(cout," "));

  //

  // d2 hinter d einfügen.

  //

  copy(d2.begin(), d2.end(), back_inserter(d));

  //

  // Ausgabe des neuen Deques.

  //

  cout << endl << endl;

  cout << "Use a back_inserter: " << endl << "   ";

  copy(d.begin(), d.end(), ostream_iterator<int>(cout," "));

  cout << endl;

  return 0;

 }

Konstruktor

back_insert_iterator (Container& x);

Konstruktor. Erzeugt eine Instanz eines mit Container x verknüpften back_insert_iterators.

Operatoren

back_insert_iterator<Container>&

operator = (const Container::value_type& value);

Fügt eine Kopie von value am Ende des Containers ein und gibt *this zurück.

back_insert_iterator<Container>&

operator* ();

Gibt *this zurück.

back_insert_iterator<Container>&

operator++ ();

back_insert_iterator<Container>

operator++ (int);

Inkrementiert den Eingabeiterator und gibt *this zurück.

Hilfsfunktion

template <class Container>

back_insert_iterator<Container>

back_inserter (Container& x)

Gibt einen back_insert_iterator zurück, der Elemente am Ende von Container x einfügt. Mit dieser Funktion können Sie Einfügeiteratoren inline erzeugen.


basic_string

String-Bibliothek

Eine Template- Klasse zur Behandlung von zeichenähnlichen Einheiten. string und wstring sind spezialisierte Versionen von basic_string für chars bzw. wchar_ts.

Spezialisierte Implementation

basic_string <char>

basic_string <wchar_t>

Syntax

#include <string>

template <class charT,

     class traits = string_char_traits<charT>,

     class Allocator = allocator>

class basic_string {

public:

// Typen

typedef traits                               traits_type;

typedef typename traits::char_type           value_type;

typedef typename Allocator::size_type        size_type;

typedef typename Allocator::difference_type  difference_type;

typedef typename Allocator::reference        reference;

typedef typename Allocator::const_reference  const_reference;

typedef typename Allocator::pointer          pointer;

typedef typename Allocator::const_pointer    const_pointer;

typedef typename Allocator::pointer          iterator;

typedef typename Allocator::const_pointer    const_iterator;

typedef reverse_iterator<const iterator,

                    value_type,

                    const reference,

                    difference_type>

const_reverse_iterator;

typedef reverse_iterator<iterator,

                    value_type,

                    reference,

                    difference_type>    reverse_iterator;

static const size_type npos = -1;

// Konstruktoren/Destruktoren

explicit basic_string(Allocator& = Allocator());

basic_string(const basic_string& str, size_type pos = 0,

             size_type n = npos, Allocator& = Allocator());

basic_string(const charT* s, size_type n,

             Allocator& = Allocator());

basic_string(const charT* s, Allocator& = Allocator());

basic_string(size_type n, charT c,

             Allocator& = Allocator());

template <class InputIterator>

basic_string(InputIterator begin, InputIterator end,

             Allocator& = Allocator());

~basic_string();

// Zuweisungsoperatoren

 basic_string& operator=(const basic_string& str);

 basic_string& operator=(const charT* s);

 basic_string& operator=(charT c);

// Iteratoren

 iterator       begin();

 const_iterator begin() const;

 iterator       end();

 const_iterator end() const;

 reverse_iterator       rbegin();

 const_reverse_iterator rbegin() const;

 reverse_iterator       rend();

 const_reverse_iterator rend() const;

// Kapazität

   size_type       size() const;

   size_type       length() const;

   size_type       max_size() const;

   void            resize(size_type n, charT c);

   void            resize(size_type n);

   size_type       capacity() const;

   void            reserve(size_type res_arg);

   bool            empty() const;

// Elementzugriff

   charT           operator[](size_type pos) const;

   reference       operator[](size_type pos);

   const_reference at(size_type n) const;

   reference       at(size_type n);

// Modifizierer

   basic_string& operator+=(const basic_string& rhs);

   basic_string& operator+=(const charT* s);

   basic_string& operator+=(charT c);

   basic_string& append(const basic_string&,

                        size_type pos = 0,

                        size_type = npos);

   basic_string& append(const charT* s, size_type n);

   basic_string& append(const charT* s);

   basic_string& append(size_type n, charT c = charT());

   template<class InputIterator>

   basic_string& append(InputIterator, InputIterator);

   basic_string& assign(const basic_string& str,

                        size_type pos = 0,

                        size_type n = npos);

   basic_string& assign(const charT* s, size_type n);

   basic_string& assign(const charT* s);

   basic_string& assign(size_type n, charT c = charT());

   template<class InputIterator>

   basic_string& assign(InputIterator, InputIterator);

   basic_string& insert(size_type pos1,

                        const basic_string& str,

                        size_type pos = 0,

                        size_type n = npos);

   basic_string& insert(size_type pos, const charT* s,

                        size_type n);

   basic_string& insert(size_type pos, const charT* s);

   basic_string& insert(size_type pos, size_type n,

                        charT c = charT());

   iterator insert(iterator p, charT c = charT());

   iterator insert(iterator p, size_type n,

                   charT c = charT() );

   template<class InputIterator>

   void insert(iterator p, InputIterator first,

               InputIterator last);

   basic_string& remove(size_type pos = 0,

                        size_type n = npos);

   basic_string& remove(iterator position);

   basic_string& remove(iterator first, iterator last);

   basic_string& replace(size_type pos, size_type n1,

                         const charT* s, size_type n2);

   basic_string& replace(size_type pos1, size_type n1,

                         const basic_string& str,

                         size_type pos2 = 0,

                         size_type n2 = npos);

   basic_string& replace(size_type pos, size_type n,

                         const charT* s);

   basic_string& replace(size_type pos, size_type n,

                         charT c = charT());

   basic_string& replace(iterator i1, iterator i2,

                         const basic_string& str);

   basic_string& replace(iterator i1, iterator i2,

                         const charT* s, size_type n);

   basic_string& replace(iterator i1, iterator i2,

                         const charT* s);

   basic_string& replace(iterator i1, iterator i2,

                         size_type n, charT c = charT() );

   template<class InputIterator>

   basic_string& replace(iterator i1, iterator i2,

                         InputIterator j1,

                         InputIterator j2);

   size_type copy(charT* s, size_type n, size_type pos = 0);

   void swap(basic_string<charT, traits, Allocator>&);

// String-Operationen

   const charT* c_str() const;

   const charT* data() const;

   size_type find(const basic_string& str,

                  size_type pos = 0) const;

   size_type find(const charT* s,

                  size_type pos, size_type n) const;

   size_type find(const charT* s, size_type pos = 0) const;

   size_type find(charT c, size_type pos = 0) const;

   size_type rfind(const basic_string& str,

                   size_type pos = npos) const;

   size_type rfind(const charT* s,

                   size_type pos, size_type n) const;

   size_type rfind(const charT* s,

                   size_type pos = npos) const;

   size_type rfind(charT c, size_type pos = npos) const;

   size_type find_first_of(const basic_string& str,

                           size_type pos = 0) const;

   size_type find_first_of(const charT* s,

                           size_type pos,

                           size_type n) const;

   size_type find_first_of(const charT* s,

                           size_type pos = 0) const;

   size_type find_first_of(charT c,

                           size_type pos = 0) const;

   size_type find_last_of(const basic_string& str,

                          size_type pos = npos) const;

   size_type find_last_of(const charT* s,

                          size_type pos, size_type n) const;

   size_type find_last_of(const charT* s,

                          size_type pos = npos) const;

   size_type find_last_of(charT c,

                          size_type pos = npos) const;

   size_type find_first_not_of(const basic_string& str,

                               size_type pos = 0) const;

   size_type find_first_not_of(const charT* s,

                               size_type pos,

                               size_type n) const;

   size_type find_first_not_of(const charT* s,

                               size_type pos = 0) const;

   size_type find_first_not_of(charT c,

                               size_type pos = 0) const;

   size_type find_last_not_of(const basic_string& str,

                              size_type pos = npos) const;

   size_type find_last_not_of(const charT* s,

                              size_type pos,

                              size_type n) const;

   size_type find_last_not_of(const charT* s,

                              size_type pos = npos) const;

   size_type find_last_not_of(charT c,

                              size_type pos = npos) const;

   basic_string substr(size_type pos = 0,

                       size_type n = npos) const;

   int compare(const basic_string& str, size_type pos = 0,

               size_type n = npos) const;

   int compare(charT* s, size_type pos, size_type n) const;

   int compare(charT* s, size_type pos = 0) const;

};

Beschreibung

basic_string<charT, traits, allocator> ist eine homogene Kollektion von zeichenähnlichen Einheiten mit der allgemeinen Funktionalität von Strings, z.B. Vergleichs-, Anhänge-, Zuweisungs-, Einfüge- und Ersetzungsfunktionen sowie verschiedenen Suchverfahren. basic_string fungiert auch als STL (StandardTemplate Library) -Sequenz-Container und stellt Random Access-Iteratoren zur Verfügung. Hierdurch können einige der generischen Algorithmen auf Strings angewandt werden.

Beliebige zugrundeliegende zeichenähnliche Typen können verwendet werden, solange eine passende Klasse string_char_traits zur Verfügung gestellt wird oder die Standardklasse traits verwendbar ist.

Beispiel

 #include<string>

 using namespace std;

 int main()

 {

   string test, result;

   //Geben Sie einen String mit mehr als fünf Zeichen ein

   while(test.empty() ||  test.size() <= 5)

   {

     cout << "Type a string between 5 and 100 characters long. "
              << endl;

     cin >> test;

   }

   //Testen des operator[]-Zugriffs

   cout << endl << "You typed in: " << test << endl << endl;

   cout << "Changing the third character from " << test[2] <<
           " to * " << endl;

   test[3] = '*';

   cout << "now its: " << test << endl << endl;

   //Einfügeelementfunktion versuchen

   cout << "Identifying the middle: ";

   test.insert(test.size() / 2, "(the middle is here!)");

   cout << test << endl << endl;

   //Ersetzen versuchen

   cout << "I didn't like the word 'middle',so instead,I'll say:"
            << endl;

   test.replace(test.find("middle",0), 6, "center");

   cout << test << endl;

   return 0;

 }

Konstruktoren und Destruktoren

In allen Fällen gibt der Parameter Allocator den Speicher für den erzeugten String an.

explicit

basic_string (const Allocator& a = Allocator());

Der Standardkonstruktor. Erzeugt einen basic_string der Länge Null.

explicit

basic_string (size_type size, const Allocator& a = Allocator());

Erzeugt einen String von size Einheiten.

basic_string (const basic_string<T, traits,
              Allocator>& s);

Erzeugt einen String, der eine Kopie von s ist.

basic_string (const basic_string<T, traits, Allocator>&s,

              size_type pos,

              const Allocator& a = Allocator());

Erzeugt einen String, der eine Kopie von s ist und bei Zeichen pos beginnt.

basic_string (const charT * s, size_type n,

              const Allocator& a = Allocator());

Erzeugt einen String, der die ersten n Zeichen von s enthält.

basic_string (const charT * s,

              const Allocator& a = Allocator());

Erzeugt einen String, der alle Zeichen in s bis zu (jedoch nicht einschließlich) einem Zeichen traits::eos() enthält. s kann kein Nullzeiger sein.

basic_string (size_type n, charT c,

              const Allocator& a  = Allocator());

Erzeugt einen String, der n Wiederholungen von c enthält.

template <class InputIterator>

basic_string  (InputIterator first, InputIterator last,

               const Allocator& a = Allocator());

Erzeugt einen basic_string der Länge last - first, der mit allen Werten gefüllt ist, die sich durch das Dereferenzieren der InputIterators im Bereich [first, last) ergeben.

~basic_string ();

Gibt den zugewiesenen Speicher für basic_string frei.

Operatoren

basic_string

operator = (const basic_string& str);

Setzt den Inhalt dieses Strings auf den Inhalt von str.

basic_string

operator = (const charT * s);

Setzt den Inhalt dieses Strings auf den Inhalt von s bis (jedoch nicht einschließlich) zum Zeichen traits::eos().

basic_string

operator = (charT c);

Setzt den Inhalt dieses Strings so, daß er gleich dem einzelnen charT c ist.

charT

operator[] (size_type pos) const;

reference

operator[] (size_type pos);

Wenn pos < size() ist, wird das Element an Position pos in diesem String zurückgeliefert. Wenn pos == size() ist, gibt die Version const traits::eos() zurück. Das Verhalten der Version nicht const ist nicht definiert. Die zurückgegebene Referenz wird durch jeden Aufruf von c_str(), data() oder jeder Elementfunktion nicht const ungültig.

basic_string&

operator += (const basic_string& s);

basic_string&

operator += (const charT* s);

basic_string&

operator += (charT c);

Verkettet einen String mit dem aktuellen Inhalt dieses Strings. Der zweite Elementoperator verwendet traits::length(), um die Anzahl der Elemente von s bis Hinzufügen festzustellen. Der dritte Elementoperator fügt das einzelne Zeichen c hinzu. Alle geben nach Beendigung eine Referenz auf diesen String zurück.

Iteratoren

iterator begin ();

iterator begin () const;

Liefert einen Iterator zurück, der mit dem ersten Element des Strings initialisiert ist.

iterator end ();

iterator end () const;

Liefert einen Iterator zurück, der mit der Position nach dem letzten Element des Strings initialisiert ist.

iterator rbegin ();

iterator rbegin () const;

Liefert einen Iterator zurück, der mit reverse_iterator(end()) äquivalent ist.

iterator rend ();

iterator rend () const;

Liefert einen Iterator zurück, der mit reverse_iterator(begin()) äquivalent ist.

Elementfunktionen

basic_string&

append (const basic_string& s, size_type

        pos = 0, size_type n = npos);

basic_string&

append (const charT* s, size_type n);

basic_string&

append (const charT* s);

basic_string&

append (charT c);

basic_string&

append (size_type n, charT c );

template<class InputIterator>

basic_string&

append (InputIterator first, InputIterator last);

Hängt einen anderen String an das Ende dieses Strings an. Die erste Funktion hängt dem kleineren der Werte n und s.size() - pos Zeichen von s an, beginnend bei Position pos bis zu diesem String. Dieses Element löst eine Exception out_of_range aus, wenn pos > str.size() ist. Das zweite Element hängt n Zeichen des Arrays an, auf das s zeigt. Die dritte Version hängt Elemente aus dem Array, auf das s zeigt, bis (jedoch nicht einschließlich) zu einem Zeichen traits::eos() an. Die vierte und fünfte Version hängen eine bzw. n Wiederholungen von c an. Die letzte Funktion append hängt die im Bereich [first, last) angegebenen Elemente an.

Alle Funktionen lösen eine Exception length_error aus, wenn die sich ergebende Länge länger als max_size() ist. Alle geben nach Beendigung eine Referenz auf diesen String zurück.

basic_string&

assign (const  basic_string& s,

       size_type pos = 0, size_type n = npos);

basic_string&

assign (const charT* s, size_type n);

basic_string&

assign (const charT* s);

basic_string&

assign (charT c);

basic_string&

assign (size_type n, charT c );

template<class InputIterator>

basic_string&

assign (InputIterator first, InputIterator last);

Ersetzt den Wert dieses Strings durch den Wert eines anderen Strings.

Alle Versionen der Funktion weisen diesem String Werte zu. Die erste Variante weist dem kleineren der Werte n und s.size() - pos Zeichen von s zu, beginnend bei Position pos. Sie löst eine Exception out_of_range aus, wenn pos > str.size() ist. Die zweite Version der Funktion weist n Zeichen des Arrays zu, auf das s zeigt. Die dritte Version weist Elemente aus dem Array, auf das s zeigt, bis (jedoch nicht einschließlich) zu einem Zeichen traits::eos() zu. Die vierte und fünfte Version weisen eine bzw. n Wiederholungen von c zu. Die letzte Version weist die durch den Bereich [first, last) angegebenen Elemente zu.

Alle Funktionen lösen eine Exception length_error aus, wenn die sich ergebende Länge länger als max_size() ist. Alle geben nach Beendigung eine Referenz auf diesen String zurück.

const_reference

at (size_type n) const;

reference

at (size_type n);

Wenn n < size() ist, wird das Element an Position n in diesem String zurückgegeben. Andernfalls wird eine Exception out_of_range ausgelöst.

size_type

capacity () const;

Gibt die aktuelle Speicherkapazität des Strings zurück. Sie ist mindestens so groß wie size().

int

compare (const basic_string& str,

         size_type pos =  0, size_type n = npos);

Gibt das Ergebnis eines lexigraphischen Vergleichs zwischen den Elementen dieses Strings und den Elementen von str zurück. Eine Exception out_of_range wird ausgelöst, wenn pos > size() ist. Der Rückgabewert ist:

traits::compare (data()+pos, str.data(),
                 min{n, size()-pos, str.size()}).

int

compare (const charT* s, size_type pos,

         size_type n) const;

int

compare (const charT* s, size_type pos = 0) const;

Gibt das Ergebnis eines lexigraphischen Vergleichs zwischen den Elementen dieses Strings und eines gegebenen Vergleichs-Strings zurück. Die Elemente geben jeweils folgendes zurück:

compare(basic_string(s, n), pos)

compare(basic_string(s), pos)

size_type

copy (charT* s, size_type n,  size_type pos = 0);

Ersetzt die Elemente im Speicher mit Kopien der Elemente aus diesem String. Eine Exception out_of_range wird ausgelöst, wenn pos > size() ist. Der kleinere der Werte n und size() - pos Elemente dieses Strings, beginnend bei Position pos, wird in das Array kopiert, auf das s zeigt. Es wird keine terminierende Null an s angehängt.

const charT*

c_str () const;

Gibt einen Zeiger auf das Anfangselement eines Arrays zurück, dessen erste Elemente size() Kopien der Elemente in diesem String sind. Ein Element traits::eos() wird an das Ende angehängt. Die Elemente des Arrays können nicht geändert werden, und der zurückgegebene Zeiger ist nur gültig, bis eine nicht const Elementfunktion dieses Strings aufgerufen wird. Wenn size() Null ist, gibt c_str() einen Zeiger auf ein Zeichen traits::eos() zurück. Siehe auch Funktion data().

const charT*

data () const;

Gibt einen Zeiger auf das Anfangselement eines Arrays zurück, dessen erste Elemente size() Kopien der Elemente in diesem String sind. Ein Element traits::eos() wird an das Ende angehängt. Die Elemente des Arrays können nicht geändert werden, und der zurückgegebene Zeiger ist nur gültig, bis eine nicht const Elementfunktion dieses Strings aufgerufen wird. Wenn size() Null ist, gibt die Funktion data() einen NULL-Zeiger zurück. Siehe auch Funktion c_str().

bool empty () const;

Gibt size() == 0 zurück.

size_type

find (const basic_string& str, size_type pos = 0) const;

Sucht, beginnend an Position pos, in diesem String nach dem ersten Vorkommen des durch str angegebenen Substrings. Bei Erfolg wird der Index des ersten Zeichens des entsprechenen Substrings zurückgegeben. Wird kein Substring gefunden, wird npos zurückgeliefert. Gleichheit wird durch traits::eq() definiert.

size_type

find (const charT* s, size_type pos, size_type n) const;

size_type

find (const charT* s, size_type pos = 0) const;

size_type

find (charT c, size_type pos = 0) const;

Sucht in diesem String nach der ersten Sequenz von Zeichen, die einem angegebenen String entsprechen. Die Varianten dieser Funktion geben folgendes zurück:

find(basic_string(s,n), pos)

find(basic_string(s), pos)

find(basic_string(1, c), pos)

size_type

find_first_not_of (const basic_string& str,

                   size_type pos = 0) const;

Sucht nach dem ersten Element in diesem String an oder nach Position pos, das nicht gleich einem beliebigen Element von str ist. Bei Erfolg gibt find_first_not_of den Index des nicht passenden Zeichens zurück. Wenn alle Zeichen eine Entsprechung besitzen, gibt die Funktion npos zurück. Gleichheit wird durch traits::eq() definiert.

size_type

find_first_not_of  (const charT* s,

                    size_type pos, size_type n) const;

size_type

find_first_not_of (const charT* s,

                   size_type pos = 0) const;

size_type

find_first_not_of (charT c, size_type pos = 0) const;

Sucht nach dem ersten Element in diesem String an oder nach Position pos, das nicht gleich einem beliebigen Element einer gegebenen Menge von Zeichen ist. Die Elemente geben folgendes zurück:

find_first_not_of(basic_string(s,n), pos)

find_first_not_of(basic_string(s), pos)

find_first_not_of(basic_string(1, c), pos)

size_type

find_first_of (const basic_string& str,

               size_type pos = 0) const;

Sucht in diesem String nach dem ersten Vorkommen eines beliebigen Elements von str an oder nach Position pos. Bei Erfolg wird der Index des passenden Zeichens zurückgegeben. Wird kein Element gefunden, wird npos zurückgegeben. Gleichheit wird durch traits::eq() definiert.

size_type

find_first_of (const charT*  s,  size_type  pos,

               size_type n) const;

size_type

find_first_of (const charT* s, size_type pos = 0) const;

size_type

find_first_of  (charT c, size_type pos = 0) const;

Sucht in diesem String nach dem ersten Vorkommen eines beliebigen Elements eines angegebenen Strings. Die Varianten von find_first_of liefern folgendes zurück:

find_first_of(basic_string(s,n), pos)

find_first_of(basic_string(s), pos)

find_first_of(basic_string(1, c), pos)

size_type

find_last_not_of (const basic_string& str,

                  size_type pos = npos) const;

Sucht nach dem letzten Element dieses Strings an oder nach Position pos, das nicht gleich einem beliebigen Element in str ist. Wenn find_last_not_of ein nicht entsprechendes Element findet, wird der Index des Zeichens zurückgegeben. Wenn alle Elemente eine Entsprechung besitzen, gibt die Funktion npos zurück. Gleichheit wird durch traits::eq() definiert.

size_type

find_last_not_of (const charT* s,

                  size_type pos, size_type n) const;

size_type

find_last_not_of (const charT* s, size_type pos = 0) const;

size_type

find_last_not_of (charT c, size_type pos = 0) const;

Sucht nach dem letzten Element dieses Strings an oder nach Position pos, das nicht gleich einem beliebigen Element einer gegebenen Menge von Zeichen ist. Die Elemente geben folgendes zurück:

find_last_not_of(basic_string(s,n), pos)

find_last_not_of(basic_string(s), pos)

find_last_not_of(basic_string(1, c), pos)

size_type

find_last_of (const basic_string& str,

              size_type pos = npos) const;

Sucht nach dem letzten Vorkommen eines beliebigen Elements von str an oder vor Position pos in diesem String. Wird ein solches gefunden, gibt find_last_of den Index des passenden Zeichens zurück. Wird kein Element gefunden, gibt find_last_of npos zurück. Gleichheit wird durch traits::eq() definiert.

size_type

find_last_of (const charT* s, size_type pos,

              size_type n) const;

size_type

find_last_of (const charT* s, size_type pos = 0) const;

size_type

find_last_of (charT c, size_type pos = 0) const;

Sucht in diesem String nach dem letzten Vorkommen eines beliebigen Elements eines angegebenen Strings. Die Elemente liefern folgendes zurück:

find_last_of(basic_string(s,n), pos)

find_last_of(basic_string(s), pos)

find_last_of(basic_string(1, c), pos)

basic_string&

insert (size_type pos, const  basic_string& s,

        size_type pos2 = 0, size_type n = npos);

basic_string&

insert (size_type pos, const charT* s, size_type n);

basic_string&

insert (size_type pos, const charT* s);

basic_strint&

insert (size_type pos, charT c);

basic_string&

insert (size_type pos, size_type n, charT c);

Fügt an Position pos zusätzliche Elemente in diesen String ein. Alle Versionen dieser Funktion lösen eine Exception out_of_range aus, wenn pos > size() ist. Alle Versionen lösen außerdem length_error aus, wenn der sich ergebende String größer als max_size() ist. Die Elemente dieses Strings werden, wenn nötig, auseinandergeschoben, um die eingefügten Elemente aufzunehmen. Alle liefern nach Beendigung eine Referenz auf diesen String zurück.

Die erste Version dieser Funktion fügt in den kleineren der Werte n und s.size() - pos2 Zeichen von s ein, beginnend an Position pos2 in diesem String. Diese Version löst eine Exception out_of_range aus, wenn pos2 > s.size() ist. Die zweite Version fügt n Zeichen des Arrays ein, auf das s zeigt. Die dritte Version fügt Elemente aus dem Array, auf das s zeigt, bis (jedoch nicht einschließlich) zu einem Zeichen traits::eos() ein. Die vierte und fünfte Version schließlich fügen eine oder n Wiederholungen von c ein.

iterator

insert (iterator p, charT c);

iterator

insert (iterator p, size_type n, charT c);

template<class InputIterator>

void

insert (iterator p, InputIterator first, InputIterator last);

Fügt in diesem String zusätzliche Elemente unmittelbar vor dem Zeichen ein, auf das sich p bezieht. Alle diese Versionen von insert erfordern, daß p eine gültiger Iterator für diesen String ist. Die erste Version fügt eine Kopie von c ein. Die zweite Version fügt n Wiederholungen von c ein. Die dritte Version fügt Zeichen im Bereich [first, last) ein. Die ersten beiden Versionen liefern p zurück.

size_type

length () const;

Gibt die Anzahl der in diesem String enthaltenen Elemente zurück.

size_type

max_size () const

Gibt die Maximalgröße des Strings zurück.

size_type

rfind (const basic_string& str, size_type pos  = npos) const;

Sucht in diesem String nach dem letzten Vorkommen des durch str angegebenen Substrings, beginnend bei Position pos. Beachten Sie, daß nur das erste Zeichen des Substrings <= pos sein muß; die restlichen Zeichen können über pos hinausreichen. Bei Erfolg wird der Index des ersten Zeichens, das Substring entspricht, zurückgegeben. Gleichheit wird durch traits::eq() definiert.

size_type

rfind (const charT* s, size_type pos,  size_type n) const;

size_type

rfind (const charT* s, size_type pos = 0) const;

size_type

rfind (charT c, size_type pos = 0) const;

Sucht in diesem String nach der letzten Zeichensequenz, die einem angegebenen String entspricht. Die Varianten von rfind geben folgendes zurück:

rfind(basic_string(s,n), pos)

rfind(basic_string(s), pos)

rfind(basic_string(1, c), pos)

basic_string&

remove (size_type pos = 0,  size_type  n  = npos);

basic_string&

remove (iterator p);

basic_string&

remove (iterator first, iterator last);

Diese Funktion entfernt Elemente aus dem String und schiebt die restlichen Elemente nach Bedarf so zusammen, daß eventuelle Leerstellen entfernt werden. Die erste Version der Funktion entfernt den kleineren der Werte n und size() - pos, beginnend bei Position pos. Eine Exception out_of_range wird ausgelöst, wenn pos > size() ist. Die zweite Version verlangt, daß p ein gültiger Iterator für diesen String ist und entfernt das Zeichen, auf das sich p bezieht. Die letzte Version von remove verlangt, daß sowohl first als auch last gültige Iteratoren für diesen String sind und entfernt die durch den Bereich [first, last) definierten Zeichen. Die Destruktoren für alle entfernten Zeichen werden aufgerufen. Alle Versionen von remove geben nach Beendigung eine Referenz auf diesen String zurück.

basic_string&

replace (size_type pos, size_type n1, const basic_string& s,

         size_type pos2 = 0, size_type n2 = npos);

basic_string&

replace (size_type pos, size_type n1, const charT* s, size_type n2);

basic_string&

replace (size_type pos, size_type n1, const charT* s);

basic_string&

replace (size_type pos, size_type n1, charT c);

Die Funktion replace ersetzt ausgewählte Elemente dieses Strings durch eine andere Elementmenge. Alle Versionen fügen die neuen Elemente anstelle von n1 Elementen in diesen String ein, beginnend bei Position pos. Sie lösen alle eine Exception out_of_range aus, wenn pos1 > size()ist sowie eine Exception length_error, wenn die sich ergebende String-Größe größer als max_size() ist.

Die erste Version ersetzt Elemente des Original-Strings durch n2 Zeichen aus String s, beginnend bei Position pos2. Eine Exception out_of_range wird ausgelöst, wenn pos2 > s.size() ist. Die zweite Version der Funktion ersetzt Elemente im Original-String durch n2 Elemente aus dem Array, auf das s zeigt. Die dritte Version ersetzt Elemente im String durch Elemente aus dem Array, auf das s zeigt, bis zu (jedoch nicht einschließlich) einem Zeichen traits::eos(). Die vierte Variante ersetzt Elemente durch n Wiederholungen des Zeichens c.

basic_string&

replace (iterator i1, iterator i2,

         const basic_string& str);

basic_string&

replace (iterator i1, iterator i2, const charT* s,

         size_type n);

basic_string&

replace (iterator i1, iterator i2, const charT* s);

basic_string&

replace (iterator i1, iterator i2, size_type n,

         charT c = charT());

template<class InputIterator>

basic_string&

replace (iterator  i1,  iterator  i2,

         InputIterator j1, InputIterator j2);

Ersetzt ausgewählte Elemente dieses Strings durch eine andere Elementmenge. Alle diese Versionen von replace erfordern, daß die Iteratoren i1 und i2 gültige Iteratoren für diesen String sind. Die durch den Bereich [i1, i2) angegebenen Elemente werden durch die neuen Elemente ersetzt.

Die erste hier vorgestellte Version ersetzt durch alle Elemente in str. Die zweite Version beginnt bei Position i1 und ersetzt die nächsten n Zeichen durch n Zeichen des Arrays, auf das s zeigt. Die dritte Variante ersetzt String-Elemente durch Elemente aus dem Array, auf das s zeigt, bis zu (jedoch nicht einschließlich) einem Zeichen traits::eos(). Die vierte Version ersetzt String-Elemente durch n Wiederholungen von c. Die letzte hier vorgestellte Variante ersetzt String-Elemente durch die durch den Bereich [j1, j2) angegebenen Elemente.

void reserve (size_type res_arg);

Stellt sicher, daß die Speicherkapazität mindestens res_arg beträgt.

void

resize (size_type n, charT c)

void

resize (size_type n)

Ändert die Kapazität dieses Strings in n. Wenn die neue Kapazität kleiner ist als die aktuelle Größe des Strings, wird er abgeschnitten. Wenn die Kapazität größer ist, wird der String mit c Zeichen aufgefüllt. Das letztgenannte Element resize füllt den String mit Standardzeichen auf, die durch traits::eos() angegeben werden.

size_type

size () const;

Gibt die Anzahl der in diesem String enthaltenen Elemente zurück.

basic_string

substr (size_type pos = 0, size_type n = npos) const;

Gibt einen String zurück, der aus Kopien des kleineren Werts von n und size() Zeichen in diesem String, beginnend bei Index pos, besteht. Eine Exception out_of_range wird ausgelöst, wenn pos <= size() ist.

void

swap (basic_string& s);

Vertauscht den Inhalt dieses Strings mit dem Inhalt von s.

Nicht-Element-Operatoren

template<class charT, class traits, class Allocator>

basic_string

operator + (const basic_string&  lhs, const basic_string& rhs)

Gibt einen String der Länge lhs.size() + rhs.size() zurück, wobei die ersten lhs.size() Elemente Kopien der Elemente von lhs sind und die nächsten rhs.size() Elemente Kopien der Elemente von rhs sind.

template<class charT, class traits, class Allocator>

basic_string

operator + (const charT* lhs, const basic_string& rhs)

template<class charT, class traits, class Allocator>

basic_string

operator + (charT lhs, const basic_string& rhs)

template<class charT, class traits, class Allocator>

basic_string

operator + (const basic_string&  lhs, const charT* rhs)

template<class charT, class traits, class Allocator>

basic_string

operator + (const basic_string& lhs, charT rhs)

Gibt einen String zurück, der die Verkettung zweier stringähnlicher Einheiten darstellt. Diese Funktionen geben folgendes zurück:

basic_string(lhs) + rhs

basic_string(1, lhs) + rhs

lhs + basic_string(rhs)

lhs + basic_string(1, rhs)

template<class charT, class traits, class Allocator>

bool

operator == (const  basic_string&  lhs,  const

basic_string& rhs)

Gibt den boolschen Wert true zurück, wenn lhs und rhs gleich sind und false, wenn sie nicht gleich sind. Gleichheit wird durch die Elementfunktion compare() definiert.

template<class charT, class traits, class Allocator>

bool

operator == (const charT* lhs,  const basic_string& rhs)

template<class charT, class traits, class Allocator>

bool

operator ==  (const  basic_string& lhs, const charT* rhs)

Liefert einen boolschen Wert zurück, der angibt, ob lhs und rhs gleich sind. Gleichheit wird durch die Elementfunktion compare() definiert. Diese Funktionen geben folgendes zurück:

basic_string(lhs) == rhs

lhs == basic_string(rhs)

template<class charT, class traits, class Allocator>

bool

operator !=  (const basic_string& lhs,

              const basic_string& rhs)

Liefert einen boolschen Wert zurück, der die Ungleichheit von lhs und rhs angibt. Ungleichheit wird durch die Elementfunktion compare() definiert.

template<class charT, class traits, class Allocator>

bool

operator != (const charT* lhs, const basic_string& rhs)

template<class charT, class traits, class Allocator>

bool

operator!= (const basic_string& lhs, const charT* rhs)

Liefert einen boolschen Wert zurück, der die Ungleichheit von lhs und rhs angibt. Ungleichheit wird durch die Elementfunktion compare() definiert. Die Elemente geben folgendes zurück:

basic_string(lhs) != rhs

lhs != basic_string(rhs)

template<class charT, class traits, class Allocator>

bool

operator <  (const  basic_string&  lhs,   const basic_string& rhs)

Liefert einen boolschen Wert zurück, der die lexigraphische Beziehung kleiner als von lhs und rhs angibt. kleiner als wird durch die Elementfunktion compare() definiert.

template<class charT, class traits, class Allocator>

bool

operator < (const charT* lhs, const basic_string& rhs)

template<class charT, class traits, class Allocator>

bool

operator < (const basic_string&  lhs, const charT* rhs)

Liefert einen boolschen Wert zurück, der die lexigraphische Beziehung kleiner als von lhs und rhs angibt. Die Elementfunktion compare() definiert kleiner als. Diese Funktionen geben folgendes zurück:

basic_string(lhs) < rhs

lhs < basic_string(rhs)

template<class charT, class traits, class Allocator>

bool

operator > (const basic_string& lhs, const basic_string& rhs)

Liefert einen boolschen Wert zurück, der die lexigraphische Beziehung größer als von lhs und rhs angibt. Die Elementfunktion compare() definiert größer als.

template<class charT, class traits, class Allocator>

bool

operator > (const charT* lhs, const basic_string& rhs)

template<class charT, class traits, class Allocator>

bool operator > (const basic_string& lhs, const charT* rhs)

Liefert einen boolschen Wert zurück, der die lexigraphische Beziehung größer als von lhs und rhs angibt. Die Elementfunktion compare() definiert größer als. Die Elemente geben folgendes zurück:

basic_string(lhs) > rhs

lhs > basic_string(rhs)

template<class charT, class traits, class Allocator>

bool

operator <= (const basic_string& lhs,

             const basic_string& rhs)

Liefert einen boolschen Wert zurück, der die lexigraphische Beziehung kleiner oder gleich von lhs und rhs angibt. Die Elementfunktion compare() definiert kleiner oder gleich.

template<class charT, class traits, class Allocator>

bool

operator <= (const charT* lhs, const

basic_string& rhs)

template<class charT, class traits, class Allocator>

bool

operator <= (const basic_string& lhs, const

charT* rhs)

Liefert einen boolschen Wert zurück, der die lexigraphische Beziehung kleiner oder gleich von lhs und rhs angibt. Die Elementfunktion compare() definiert kleiner oder gleich. Diese Funktionen geben folgendes zurück:

basic_string(lhs) <= rhs

lhs <= basic_string(rhs)

template<class charT, class traits, class Allocator>

bool

operator >= (const basic_string& lhs,

             const basic_string& rhs)

Liefert einen boolschen Wert zurück, der die lexigraphische Beziehung größer oder gleich von lhs und rhs angibt. Die Elementfunktion compare() definiert größer oder gleich.

template<class charT, class traits, class Allocator>

bool

operator >= (const charT* lhs, const basic_string& rhs)

template<class charT, class traits, class Allocator>

bool

operator >= (const basic_string& lhs, const charT* rhs)

Liefert einen boolschen Wert zurück, der die lexigraphische Beziehung größer oder gleich von lhs und rhs angibt. Die Elementfunktion compare() definiert größer oder gleich. Die Elemente geben folgendes zurück:

basic_string(lhs) >= rhs

lhs >= basic_string(rhs)

template<class charT, class traits, class Allocator>

basic_istream<charT>&

operator >> (basic_istream<charT>& is,

             basic_string<charT,

             traits, Allocator>& str);

Liest basic_string<charT, traits, Allocator> aus is mittels traits::char_in, bis ein Element traits::is_del() gelesen wird. Alle gelesenen Elemente - mit Ausnahme des Begrenzers - werden in str eingefügt. Nach dem Lesevorgang gibt die Funktion is zurück.

template<class charT, class traits, class Allocator>

basic_ostream<charT>&

operator << (basic_ostream<charT>& os,

             const basic_string<charT,

             traits, Allocator>& str);

Schreibt alle Elemente in der Reihenfolge vom ersten bis zum letzten Element von str nach os und verwendet dabei traits::char_out(). Nach dem Schreibvorgang gibt die Funktion os zurück.

Nicht-Elementfunktion

template <class charT, class IS_traits, class STR_traits,

          class STR_Alloc>

basic_istream<charT, IS_traits>&

getline (basic_istream<charT, IS_traits>& is,

         basic_string<charT, STR_traits, STR_Alloc>& str,

         charT delim = IS_traits::newline() );

Eine unformatierte Eingabefunktion, die Zeichen aus is nach str extrahiert, bis npos - 1 Zeichen gelesen wurden, das Ende der Eingabesequenz erreicht wurde oder das gelesene Zeichen delim ist. Die Zeichen werden mittels STR_traits::char_in() gelesen.

Wenn der neue iostreams auf Ihrem System nicht zur Verfügung steht, können Sie die äquivalenten Funktionen operator >>, operator << und getline verwenden, die mit dem alten iostreams arbeiten.


Bidirektionaler Iterator

Siehe auch	Iterator

Ein Iterator, der sowohl lesen als auch schreiben und sich in beiden Richtungen durch den Container bewegen kann.

Beschreibung

Hinweis:	Eine vollständige Beschreibung der Iteratoren finden Sie im Abschnitt Iteratoren dieses Referenzteils.

Iteratoren sind eine Generalisierung von Zeigern, durch die ein C++ Programm einheitlich mit verschiedenen Datenstrukturen interagieren kann. Bidirektionale Iteratoren können sich sowohl vorwärts als auch rückwärts durch einen Container bewegen. Sie besitzen die Fähigkeit, Daten zu lesen und zu schreiben. Diese Iteratoren genügen den unten aufgeführten Anforderungen.

Der folgende Schlüssel bezieht sich auf die unten aufgeführten Iteratorbeschreibungen:

a und b	Werte vom Typ X

n	Wert vom Typ distance

u, Distance, tmp und m	Bezeichner

r	Wert vom Typ X&

t	Wert vom Typ T

Anforderungen an bidirektionale Iteratoren

Ein bidirektionaler Iterator muß alle unten genannten Anforderungen erfüllen. Beachten Sie, daß die meisten dieser Anforderungen auch die Anforderungen an Vorwärtsiteratoren sind.

X u	u kann einen singulären Wert besitzen

X()	X() kann singulär sein

X(a)	Kopierkonstruktor, a == X(a).

X u(a)	Kopierkonstruktor, u == a

X u = a	Zuweisung, u == a

a == b, a != b	Rückgabewert in bool umwandelbar

*a	Rückgabewert in T& umwandelbar

++r	gibt X& zurück

r++	Rückgabewert in const X& umwandelbar

*r++	gibt T& zurück

--r	gibt X& zurück

r--	Rückgabewert in const X& umwandelbar

*r--	gibt T& zurück

Ebenso wie Vorwärtsiteratoren unterliegen bidirektionale Iteratoren der Bedingung, daß a == b *a== *b impliziert.

Die Anzahl der Durchgänge, die ein Algorithmus in der Struktur durchführt, unterliegt keiner Einschränkung.


binary_function

Siehe auch	Funktionsobjekt

Abstrakte Basisfunktion für binäre Funktionsobjekte.

Syntax

#include <functional>

template <class Arg1, class Arg2, class Result>

    struct binary_function{

         typedef Arg1 first_argument_type;

         typedef Arg2 second_argument_type;

         typedef Result result_type;

    };

Beschreibung

Funktionsobjekte sind Objekte mit einem definierten operator(). Sie sind wichtig für die effiziente Verwendung der generischen Algorithmen der Standardbibliothek, da die Schnittstelle für jedes algorithmische Template entweder ein Objekt mit einem definierten operator() oder einen Zeiger auf eine Funktion akzeptieren kann. Die Standardbibliothek stellt sowohl eine Standardmenge von Funktionsobjekten als auch zwei Klassen zur Verfügung, die Sie als Grundlage für die Erzeugung Ihrer eigenen Funktionsobjekte verwenden können.

Funktionsobjekte, die zwei Argumente annehmen, werden binäre Funktionsobjekte genannt. Binäre Funktionsobjekte stellen die Typdefinitionen first_argument_type, second_argument_type und result_type zur Verfügung. Die Klasse binary_function vereinfacht die Erzeugung binärer Funktionsobjekt-Templates, indem sie die erforderlichen Typdefinitionen für ein binäres Funktionsobjekt zur Verfügung stellt. Sie können über Vererbung von binary_function Ihre eigenen binären Funktionsobjekte erzeugen.


binary_negate

Siehe auch	Funktionsobjekt

Funktionsobjekt, das das Komplement des Ergebnisses seines binären Prädikats zurückgibt.

Syntax

#include <functional>

template<class Predicate>

class binary_negate

  : public binary_function<Predicate::first_argument_type,

                           Predicate::second_argument_type,

                           bool                            >

{

public:

   explicit binary_negate (const Predicate& pred);

   bool operator() (const first_argument_type& x,

                    const second_argument_type& y) const;

};

template <class Predicate>

binary_negate<Predicate> not2 (const Predicate& pred);

Beschreibung

binary_negate ist eine Funktionsobjektklasse, die einen Rückgabetyp für den Funktionsadapter not2 zur Verfügung stellt. not2 ist ein Funktionsadapter, den man auch als Negator bezeichnet. Er nimmt ein binäres Prädikatfunktionsobjekt als Argument an und gibt ein binäres Prädikatfunktionsobjekt zurück, das das Komplement des Originals ist.

Beachten Sie, daß not2 nur mit Funktionsobjekten funktioniert, die als Unterklassen der Klasse binary_function definiert sind.

Konstruktor

explicit binary_negate (const Predicate& pred);

Erzeugt ein Objekt binary_negate aus dem Prädikat pred.

Operator

bool operator() (const first_argument_type& x,

                 const second_argument_type& y) const;

Liefert das Ergebnis von pred(x,y) zurück.


binary_search

Siehe auch	Algorithmus

Führt eine binäre Suche nach einem Wert in einem Container durch.

Syntax

#include <algorithm>

template <class ForwardIterator, class T>

    bool

    binary_search(ForwardIterator first, ForwardIterator last,

                  const T& value);

 template <class ForwardIterator, class T, class Compare>

    bool

    binary_search(ForwardIterator first, ForwardIterator last,

                  const T& value, Compare comp);

Beschreibung

Der Algorithmus binary_search führt, ebenso wie andere, verwandte Algorithmen (equal_range, lower_bound und upper_bound), eine binäre Suche in geordneten Containern durch. Alle binären Suchalgorithmen liegen in zwei Versionen vor. Die erste Version verwendet den Operator kleiner als (operator <), um den Vergleich vorzunehmen und geht dabei davon aus, daß die Sequenz mit diesem Operator sortiert wurde. Die zweite Version erlaubt das Einbinden eines Funktionsobjekts vom Typ Compare, von dem angenommen wird, daß die Sequenz mit dieser Funktion sortiert wurde. Das Funktionsobjekt muß ein binäres Prädikat sein.

Der Algorithmus binary_search gibt true zurück, wenn eine Sequenz ein Element enthält, das äquivalent mit dem Argument value ist. Die erste Version von binary_search gibt true zurück, wenn die Sequenz mindestens ein Element enthält, das gleich dem Suchwert ist. Die zweite Version des Algorithmus binary_search gibt true zurück, wenn die Sequenz mindestens ein Element enthält, das den Bedingungen der Vergleichsfunktion genügt. Formal gibt binary_search true zurück, wenn ein Iterator i im Bereich [first, last) existiert, der den entsprechenden Bedingungen genügt:

!(*i < value) && !(value < *i)

oder

comp(*i, value)  == false && comp(value, *i) == false

binary_search führt höchsten log(last - first) + 2 Vergleiche durch.

Beispiel

 #include <vector>

 #include <algorithm>

 using namespace std;

 int main()

 {

   typedef vector<int>::iterator iterator;

   int d1[10] = {0,1,2,2,3,4,2,2,6,7};

   // Erzeugen eines Vektors

   vector<int> v1(d1,d1 + 10);

   // binary_search-Varianten versuchen

   bool b1 = binary_search(v1.begin(),v1.end(),3);

   // b1 = true

   bool b2 =

      binary_search(v1.begin(),v1.end(),11,less<int>());

   // b = false

   // Ausgabe der Ergebnisse

   cout << "In the vector: ";

   copy(v1.begin(),v1.end(),

           ostream_iterator<int>(cout," "));

   cout << endl << "The number 3 was "

        << (b1 ? "FOUND" : "NOT FOUND");

   cout << endl << "The number 11 was "

        << (b2 ? "FOUND" : "NOT FOUND") << endl;

   return 0;

 }


bind1st, bind2nd, binder1st, binder2nd

Funktionsobjekt

Hilfs-Templates, die dazu dienen, Werte an Funktionsobjekte zu binden.

Syntax

#include <functional>

// Class binder1st template <class Operation>

 class binder1st
   : public unary_function<Operation::second_argument_type,

                           Operation::result_type> {

protected:

   Operation op; argument_type value;

public:

   binder1st(const Operation& x, const
             Operation::first_argument_type& y) : op(x). value(y) {}

   result_type operator() (const argument_type& x) const

};

// Creator bind1st template<class Operation, class T>

 binder1st <Operation> bind1st(const Operation& op, const T& x)

// Class binder2nd template <class Operation>

 class binder2nd
   : public unary_function<Operation::first_argument_type,
                           Operation::result_type> {

protected:

   Operation op; argument_type value;

public:

   binder2nd(const Operation& x, const

             Operation::second_argument_type& y) : op(x). value(y) {}

   result_type operator() (const argument_type& x) const

};

// Creator bind2nd template<class Operation, class T>

 binder2nd <Operation> bind2nd(const Operation& op, const T& x)

Beschreibung

Da sehr viele der Funktionen der Standardbibliothek andere Funktionen als Argumente annehmen, enthält die Bibliothek Klassen, die das Erzeugen neuer Funktionsobjekte aus alten erlauben. Sowohl bind1st() als auch bind2nd() sind Funktionen, die ein binäres Funktionsobjekt f und einen Wert x als Argumente annehmen und die Klassen binder1st bzw. binder2nd zurückgeben. Die Klasse binder1st bindet den Wert des ersten Arguments der binären Funktion, binder2nd das zweite Argument der Funktion. Die sich ergebenden Klassen können anstelle eines unären Prädikats in anderen Funktionsaufrufen verwendet werden.

Die Elementfunktionen bind1st() und bind2nd() werden inline verwendet, um die unären Prädikate zu erzeugen. Sie können zum Beispiel den Algorithmus count_if verwenden, um alle Elemente in einem Vektor zu zählen, die kleiner oder gleich 7 sind. Gehen Sie dazu folgendermaßen vor:

count_if (v.begin, v.end, bind1st(greater <int> (),7), littleNums)

Diese Funktion addiert immer, wenn das Prädikat true ist (d.h., wenn 7 größer als das Element ist), Eins zu littleNums.

Beispiel

 #include <functional>

 #include <algorithm>

 #include <vector>

 using namespace std;

 int main()

 {

   typedef vector<int>::iterator iterator;

   int d1[4] = {1,2,3,4};

   // Erzeugen eines Vektors

   vector<int> v1(d1,d1 + 4);

   // Erzeugen eines unären Prädikats 'gleich 3' durch Binden von 3 an

   // das binäre Prädikat equal_to.

   binder1st<equal_to<int> > equal_to_3 =

      bind1st(equal_to<int>(),3);

   // Verwenden des neuen Prädikats in einem Aufruf von find_if

   iterator it1 = find_if(v1.begin(),v1.end(),equal_to_3);

   // it1 = v1.begin() + 2

   // Besser: neues Prädikat nebenbei erzeugen

   iterator it2 =

      find_if(v1.begin(),v1.end(),bind1st(equal_to<int>(),3));

   // it2 = v1.begin() + 2

   // Noch einmal das Gleiche mit bind2nd

   // Gleiches Ergebnis, da == kommutativ ist

   iterator it3 =

      find_if(v1.begin(),v1.end(),bind2nd(equal_to<int>(),3));

   // it3 = v1.begin() + 2

   // Ausgabe der Ergebnisse

   cout << *it1 << " " << *it2 << " " << *it3 << endl;

   return 0;

 }


bitset

Container

Eine Template-Klasse und zugehörige Funktionen zum Speichern und Manipulieren von Bit-Sequenzen fester Größe.

Syntax

#include <bitset>

template <size_t N>

 class bitset {

public:

// Bit-Referenz:

 class reference {

 public:

  ~reference()

  reference& operator=(bool x);

  reference& operator=(const reference&);

  bool operator~() const;

  operator bool() const;

  reference& flip();

 };

// Konstruktoren

 bitset ();

 bitset (unsigned long val);

 explicit bitset (const string& str,

         size_t pos = 0,

         size_t n = size_t(-1));

// Bitweise Operatoren und bitweise Operatorzuweisung

  bitset<N>& operator&= (const bitset<N>& rhs);

  bitset<N>& operator|= (const bitset<N>& rhs);

  bitset<N>& operator^= (const bitset<N>& rhs);

  bitset<N>& operator<<= (size_t pos);

  bitset<N>& operator>>= (size_t pos);

// Set, Reset, Flip

  bitset<N>& set ();

  bitset<N>& set (size_t pos, int val = 1);

  bitset<N>& reset ();

  bitset<N>& reset (size_t pos);

  bitset<N> operator~() const;

  bitset<N>& flip ();

  bitset<N>& flip (size_t pos);

// Elementzugriff

  reference operator[] (size_t pos);

  unsigned long to_ulong() const;

  string to_string() const;

  size_t count() const;

  size_t size() const;

  bool operator== (const bitset<N>& rhs) const;

  bool operator!= (const bitset<N>& rhs) const;

  bool test (size_t pos) const;

  bool any() const;

  bool none() const;

  bitset<N> operator<< (size_t pos) const;

  bitset<N> operator>> (size_t pos) const;

private:

//Implementierung

};

// Bitset-Operatoren

template <size_t N>

bitset<N> operator& (const bitset<N>&, const bitset<N>&);

template <size_t N>

bitset<N> operator| (const bitset<N>&, const bitset<N>&);

template <size_t N>

bitset<N> operator^ (const bitset<N>&, const bitset<N>&);

template <size_t N>

istream& operator>> (istream& is, bitset<N>& x);

template <size_t N>

ostream& operator<< (ostream& os, const bitset<N>& x);

Beschreibung

bitset<N> ist eine Klasse, die Objekte beschreibt, die eine aus einer festen Anzahl von Bits, N, bestehende Sequenz speichern können. Jedes Bit stellt entweder den Wert Null (reset) oder Eins (set) dar und besitzt eine nicht negative Position pos.

Fehler und Exceptions

Bitset-Konstruktoren und -Elementfunktionen können die folgenden drei Fehlertypen melden - jeder ist mit einer eindeutigen Exception verbunden:

Fehler ungültiges Argument oder Exception invalid_argument()

Fehler außerhalb des Bereichs oder Exception out_of_range()

Fehler Überlauf oder Exception over-flow_error()

Wenn Exceptions von Ihrem Compiler nicht unterstützt werden, erhalten Sie einen Assertion-Fehler statt einer Exception.

Konstruktoren

bitset();

Erzeugt ein Objekt der Klasse bitset<N> und initialisiert alle Bit-Werte mit Null.

bitset(unsigned long val);

Erzeugt ein Objekt der Klasse bitset<N> und initialisiert die ersten M Bit-Werte mit den entsprechenden Bits in val. M ist der kleinere Wert aus N und dem Wert von CHAR_BIT * sizeof(unsigned long). Wenn M < N ist, werden die restlichen Bit-Positionen mit Null initialisiert. Hinweis: CHAR_BIT ist in <climits> definiert.

explicit

bitset (const string& str, size_t pos = 0,

    size_t n = size_t(-1));

Legt die effektive Länge rlen des Initialisierungs-Strings als den kleineren der Werte n und str.size() - pos fest. Die Funktion löst eine Exception invalid_argument aus, wenn eines der Zeichen rlen in str, beginnend bei Position pos, ungleich 0 oder 1 ist. Andernfalls erzeugt die Funktion ein Objekt der Klasse bitset<N> und initialisiert die ersten M Bit-Positionen mit Werten, die durch die entsprechenden Zeichen im String str festgelegt werden. M ist der kleinere der Werte N und rlen. Die Konstruktor verlangt, daß pos <=  str.length() ist. Andernfalls wird eine Exception out_of_range ausgelöst.

Operatoren

bool

operator == (const bitset<N>& rhs) const;

Gibt true zurück, wenn der Wert jedes Bits in *this gleich dem Wert jedes entsprechenden Bits in rhs ist. Andernfalls wird false zurückgegeben.

bool

operator != (const bitset<N>& rhs) const;

Gibt true zurück, wenn der Wert jedes Bits in *this nicht gleich dem Wert jedes entsprechenden Bits in rhs ist. Andernfalls wird false zurückgegeben.

bitset<N>&

operator &= (const bitset<N>& rhs);

Löscht jedes Bit in *this, für welches das entsprechende Bit in rhs gelöscht ist, und läßt alle anderen Bits unverändert.

bitset<N>&

operator |= (const bitset<N>& rhs);

Setzt jedes Bit in *this, für welches das entsprechende Bit in rhs gesetzt ist, und läßt alle anderen Bits unverändert.

bitset<N>&

operator ^= (const bitset<N>& rhs);

Schaltet jedes Bit in *this um, für welches das entsprechende Bit in rhs gesetzt ist, und läßt alle anderen Bits unverändert.

bitset<N>&

operator <<= (size_t pos);

Ersetzt jedes Bit an Position I durch 0, wenn I < pos ist, oder durch den Wert des Bits an Position I - pos, wenn I >= pos ist.

bitset<N>&

operator >>= (size_t pos);

Ersetzt jedes Bit an Position I durch 0, wenn pos >= N-I ist, oder durch den Wert des Bits an Position I + pos, wenn pos < N-I ist.

bitset<N>&

operator >> (size_t pos) const;

Gibt bitset<N>(*this) >>= pos zurück.

bitset<N>&

operator << (size_t pos) const;

Gibt bitset<N>(*this) <<= pos zurück.

bitset<N>

operator ~ ();

Gibt das Bitset zurück, welches das logische Komplement jedes Bits in *this ist.

bitset<N>

operator & (const bitset<N>& lhs,

     const bitset<N>& rhs );

lhs ermittelt das logische AND von lhs mit rhs.

bitset<N>

operator | (const bitset<N>& lhs,

      const bitset<N>& rhs );

lhs ermittelt das logische OR von lhs mit rhs.

bitset<N>

operator ^ (const bitset<N>& lhs,

      const bitset<N>& rhs );

lhs ermittelt das logische XOR von lhs mit rhs.

template <size_t N>

istream&

operator >> (istream& is, bitset<N>& x);

Extrahiert bis zu N Zeichen (single-byte) aus is. Speichert diese Zeichen in einem temporären Objekt str vom Typ string und wertet anschließend den Ausdruck x = bitset<N>(str) aus. Zeichen werden extrahiert und gespeichert, bis eines der folgenden Ereignisse eintritt:

N Zeichen wurden extrahiert und gespeichert.

Ein end-of-file (Dateiende) tritt in der Eingabesequenz auf.

Das nächste Zeichen ist weder '0' noch '1'. In diesem Fall wird das Zeichen nicht extrahiert.

template <size_t N>

ostream&

operator << (ostream& os, const bitset<N>& x);

Gibt os << x.to_string() zurück.

Elementfunktionen

bool

any () const;

Gibt true zurück, wenn ein Bit in *this gesetzt ist. Andernfalls wird false zurückgegeben.

size_t

count () const;

Gibt das Zählergebnis der in *this gesetzten Bits zurück.

bitset<N>&

flip();

Kehrt alle Bits in *this um und gibt *this zurück.

bitset<N>&

flip (size_t pos) const;

Kehrt das Bit an Position pos in *this um. Löst eine Exception out_of_range aus, wenn pos keiner gültigen Bit-Position entspricht.

bool

none () const;

Gibt true zurück, wenn kein Bit in *this gesetzt ist. Andernfalls wird false zurückgegeben.

bitset<N>&

reset();

Setzt alle Bits in *this zurück und gibt *this zurück.

bitset<N>&

reset (size_t pos);

Setzt das Bit an Position pos in *this zurück. Löst eine Exception out_of_range aus, wenn pos keiner gültigen Bit-Position entspricht.

bitset<N>&

set();

Setzt alle Bits in *this und gibt *this zurück.

bitset<N>&

set (size_t pos, int val = 1);

Speichert einen neuen Wert in den Bits an Position pos in *this. Wenn val ein Wert ungleich Null ist, ist der gespeicherte Wert Eins, andernfalls ist er Null. Löst eine Exception out_of_range aus, wenn pos keiner gültigen Bit-Position entspricht.

size_t

size () const;

Gibt den Template-Parameter N zurück.

bool

test (size_t pos) const;

Gibt true zurück, wenn das Bit an Position pos gesetzt ist. Löst eine Exception out_of_range aus, wenn pos keiner gültigen Bit-Position entspricht.

string

to_string() const;

Gibt ein Objekt vom Typ string zurück, das N Zeichen lang ist.

Jede Position des neuen Strings wird mit einem Zeichen ("0" für Null und "1" für Eins) initialisiert, das den Wert darstellt, der an der entsprechenden Bit-Position von *this gespeichert ist. Zeichenposition N - 1 entspricht Bit-Position 0. Die aufeinanderfolgenden absteigenden Zeichenpositionen entsprechen aufsteigenden Bit-Positionen.

unsigned long

to_ulong() const;

Gibt den Integerwert zurück, der den Bits in *this entspricht. Löst einen overflow_error aus, wenn diese Bits nicht als Typ unsigned long dargestellt werden können.


compare

Eine binäre Funktion oder ein Funktionsobjekt, das true oder false zurückgibt. "compare" wird zum Ordnen von Elementen verwendet.


complex

Bibliothek der komplexen Zahlen

C++ Bibliothek der komplexen Zahlen.

Spezialisierte Implementation

complex <float>

complex <double>

complex <long double>

Syntax

#include <complex>

template <class T>

class complex {

public:

   complex ();

   complex (T re);

   complex (T re , T im);

   template <class X> complex

    (const complex<X>&);

   T real () const;

   T imag () const;

   template <class X>

    complex<T> operator= (const complex<X>&);

   template <class X>

    complex<T> operator+= (const complex<X>&);

   template <class X>

    complex<T> operator-= (const complex<X>&);

   template <class X>

    complex<T> operator*= (const complex<X>&);

   template <class X>

    complex<T> operator/= (const complex<X>&);

};

// Operatoren

template<class T>

 complex<T> operator+

  (const complex<T>&, const complex<T>&);

template<class T>

 complex<T> operator+

  (const complex<T>&, T);

template<class T>

 complex<T> operator+

  (T, const complex<T>&);

template<class T>

 complex<T> operator-

  (const complex<T>&, const complex<T>&);

template<class T>

 complex<T> operator-

  (const complex<T>&, T);

template<classT>

 complex<T> operator-

  (T, const complex<T>&);

template<class T>

 complex<T> operator*

  (const complex<T>&, const complex<T>&);

template<class T>

 complex<T> operator*

  (const complex<T>&, T);

template<class T>

 complex<T> operator*

  (T, const complex<T>&);

template<class T>

 complex<T> operator/

  (const complex<T>&, const complex<T>&);

template<class T>

 complex<T> operator/

  (const complex<T>&, T);

template<class T>

 complex<T> operator/

  (T, const complex<T>&);

template<class T>

 complex<T> operator+

  (const complex<T>&);

template<class T>

 complex<T> operator-

  (const complex<T>&);

template<class T>

 bool operator==

  (const complex<T>&, const complex<T>&);

template<class T>

 bool operator==

  (const complex<T>&, T);

template<class T>

 bool operator==

  (T, const complex<T>&);

template<class T>

 bool operator!=

  (const complex<T>&, const complex<T>&);

template<class T>

 bool operator!=

  (const complex<T>&, T);

template<class T>

 bool operator!=

  (T, const complex<T>&);

template <class X>

 istream& operator>>

  (istream&, complex<X>&);

template <class X>

 ostream& operator<<

  (ostream&, const complex<X>&);

// Werte

template<class T> T real

 (const complex<T>&);

template<class T> T imag

 (const complex<T>&);

template<class T> T abs

 (const complex<T>&);

template<class T> T arg

 (const complex<T>&);

template<class T> T norm

 (const complex<T>&);

template<class T> complex<T> conj

 (const complex<T>&);

template<class T> complex<T> polar

 (T, T);

// Transzendente

template<class T> complex<T> acos

 (const complex<T>&);

template<class T> complex<T> asin

 (const complex<T>&);

template<class T> complex<T> atan

 (const complex<T>&);

template<class T> complex<T> atan2

 (const complex<T>&, const complex<T>&);

template<class T> complex<T> atan2

 (const complex<T>&, T);

template<class T> complex<T> atan2

 (T, const complex<T>&);

template<class T> complex<T> cos

 (const complex<T>&);

template<class T> complex<T> cosh

 (const complex<T>&);

template<class T> complex<T> exp

 (const complex<T>&);

template<class T> complex<T> log

 (const complex<T>&);

template<class T> complex<T> log10

 (const complex<T>&);

template<class T> complex<T> pow

 (const complex<T>&, int);

template<class T> complex<T> pow

 (const complex<T>&, T);

template<class T> complex<T> pow

 (const complex<T>&, const complex<T>&);

template<class T> complex<T> pow

 (T, const complex<T>&);

template<class T> complex<T> sin

 (const complex<T>&);

template<class T> complex<T> sinh

 (const complex<T>&);

template<class T> complex<T> sqrt

 (const complex<T>&);

template<class T> complex<T> tan

 (const complex<T>&);

template<class T> complex<T> tanh

 (const complex<T>&);

Beschreibung

complex<T> ist eine Klasse, die komplexe Zahlen unterstützt. Eine komplexe Zahl besteht aus einem reellen und einem imaginären Teil. Die Klasse complex unterstützt Gleichheit, Vergleich und arithmetische Grundoperationen. Zusätzlich sind auch mathematische Funktionen (beispielsweise Exponenten, Logarithmen, Potenzen und Quadratwurzeln) verfügbar.

Warnung:	Mit Compilern, die keine Elementfunktion-Templates unterstützen, funktionieren die arithmetischen Operatoren nicht mit allen arbiträren Typen. (Sie arbeiten dort nur mit den Typen float, double und long double.) Außerdem können Sie binäre Arithmetik nur mit Typen verwenden, die gleich sind. Compiler, die nicht umwandelnde Konstruktoren nicht unterstützen, erlauben unsichere Umwandlungen zum abgeleiteten Typ (d.h. long double zu double, double zu float, long double zu float).

Beispiel

 #include <complex>

 using namespace std;

 int main()

 {

   complex<double> a(1.2, 3.4);

   complex<double> b(-9.8, -7.6);

   a += b;

   a /= sin(b) * cos(a);

   b *= log(a) + pow(b, a);

   cout << "a = " << a << ", b = " << b << endl;

   return 0;

 }

Konstruktoren

complex

(const T& re_arg = 0, const T& im_arg = 0);

Erzeugt ein Objekt der Klasse complex und initialisiert re_arg mit dem reellen Teil und im_arg mit dem imaginären Teil.

template <class X> complex

(const complex<X>&);

Kopierkonstruktor. Erzeugt eine komplexe Zahl aus einer anderen komplexen Zahl.

Zuweisungsoperatoren

template <class X>

complex<T>

operator = (const complex<X>& c);

Zuweisungsoperator. Weist c sich selbst zu.

template <class X>

complex<T>

operator += (const complex<X>& c);

Fügt c sich selbst hinzu und gibt anschließend das Ergebnis zurück.

template <class X>

complex<T>

operator-= (const complex<X>& c);

Subtrahiert c von sich selbst und gibt anschließend das Ergebnis zurück.

template <class X>

complex<T>

operator *= (const complex<X>& c);

Multipliziert sich selbst mit c und gibt anschließend das Ergebnis zurück.

template <class X>

complex<T>

operator /= (const complex<X>& c);

Dividiert sich selbst durch c und gibt anschließend das Ergebnis zurück.

Operatoren

template<class T> complex<T>

operator + (const complex<T>& lhs,const complex<T>& rhs);

template<class T> complex<T>

operator + (const complex<T>& lhs, T rhs);

template<class T> complex<T>

operator + (T lhs, const complex<T>& rhs);

Gibt die Summe von lhs und rhs zurück.

template<class T> complex<T>

operator - (const complex<T>& lhs,const complex<T>& rhs);

template<class T> complex<T>

operator - (const complex<T>& lhs, T rhs);

template<class T> complex<T>

operator - (T lhs, const complex<T>& rhs);

Gibt die Differenz von lhs und rhs zurück.

template<class T> complex<T>

operator * (const complex<T>& lhs,const complex<T>& rhs);

template<class T> complex<T>

operator * (const complex<T>& lhs, T rhs);

template<class T> complex<T>

operator * (T lhs, const complex<T>& rhs);

Gibt das Produkt von lhs und rhs zurück.

template<class T> complex<T>

operator / (const complex<T>& lhs,const complex<T>& rhs);

template<class T> complex<T>

operator / (const complex<T>& lhs, T rhs);

template<class T> complex<T>

operator / (T lhs, const complex<T>& rhs);

Gibt den Quotienten von lhs dividiert durch rhs zurück.

template<class T> complex<T>

operator + (const complex<T>& rhs);

Gibt rhs zurück.

template<class T> complex<T>

operator - (const complex<T>& lhs);

Gibt complex<T>(- lhs.real,() - lhs.imag()) zurück.

template<class T> bool

operator == (const complex<T>& x, const complex<T>& y);

Gibt true zurück, wenn die reellen und imaginären Teile von x und y gleich sind.

template<class T> bool

operator == (const complex<T>& x, T y);

Gibt true zurück, wenn y gleich dem reellen Teil von x ist und der imaginäre Teil von x gleich 0 ist.

template<class T> bool

operator == (T x, const complex<T>& y);

Gibt true zurück, wenn x gleich dem reellen Teil von y ist und der imaginäre Teil von y gleich 0 ist.

template<class T> bool

operator != (const complex<T>& x, const complex<T>& y);

Gibt true zurück, wenn entweder der reelle oder der imaginäre Teil von x und y nicht gleich sind.

template<class T> bool

operator != (const complex<T>& x, T y);

Gibt true zurück, wenn y nicht gleich dem reellen Teil von x ist oder der imaginäre Teil von x nicht gleich 0 ist.

template<class T> bool

operator != (T x, const complex<T>& y);

Gibt true zurück, wenn x nicht gleich dem reellen Teil von y ist oder der imaginäre Teil von y nicht gleich 0 ist.

template <class X> istream&

operator >> (istream& is, complex<X>& x);

Liest eine komplexe Zahl x in den Eingabe-Stream is. x kann die Form u, (u) oder (u,v) besitzen, wobei u der reelle Teil ist und v der imaginäre Teil. Wenn eine fehlerhafte Eingabe auftritt, wird ios::badbit flag gesetzt.

template <class X> ostream&

operator << (ostream& os, const complex<X>& x);

Gibt os << "(" << x.real() << "," << x.imag() << ")" zurück.

Elementfunktionen

template<class T> T

abs (const complex<T>& c);

Gibt den absoluten Wert oder die Größe von c (die Quadratwurzel des Normals) zurück.

template<class T> complex<T>

acos (const complex<T>& c);

Gibt den Arkuskosinus von c zurück.

template<class T> T

arg (const complex<T>& c);

Gibt den Phasenwinkel von c zurück.

template<class T> complex<T>

asin (const complex<T>& c);

Gibt den Arkussinus von c zurück.

template<class T> complex<T>

atan (const complex<T>& c);

Gibt den Arkustangens von c zurück.

template<class T> complex<T>

atan2 (T a, const complex<T>& b);

Gibt den Arkustangens von a/b zurück.

template<class T> complex<T>

atan2 (const complex<T>& a, T b);

Gibt den Arkustangens von a/b zurück.

template<class T> complex<T>

atan2 (const complex<T>& a, const complex<T>& b);

Gibt den Arkustangens von a/b zurück.

template<class T> complex<T>

conj (const complex<T>& c);

Gibt die Konjugation von c zurück.

template<class T> complex<T>

cos (const complex<T>& c);

Gibt den Kosinus von c zurück.

template<class T> complex<T>

cosh (const complex<T>& c);

Gibt den hyperbolischen Kosinus von c zurück.

template<class T> complex<T>

exp (const complex<T>& x);

Gibt e erhoben in die Potenz x zurück.

T

imag() const;

Gibt den imaginären Teil der komplexen Zahl zurück.

template<class T> T

imag (const complex<T>& c) const;

Gibt den imaginären Teil von c zurück.

template<class T> complex<T>

log (const complex<T>& x);

Gibt den natürlichen Logarithmus von x zurück.

template<class T> complex<T>

log10 (const complex<T>& x);

Gibt die Logarithmusbasis 10 von x zurück.

template<class T> T

norm (const complex<T>& c);

Gibt die Größe von c im Quadrat zurück. (Die Summe der Quadrate des reellen und des imaginären Teils.)

template<class T> complex<T>

polar (const T& m, const T& a);

Gibt den komplexen Wert einer komplexen Zahl zurück, deren Größe m und deren Phasenwinkel a ist, gemessen im Bogenmaß.

template<class T> complex<T>

pow (const complex<T>& x, int y);

template<class T> complex<T>

pow (const complex<T>& x, T y);

template<class T> complex<T>

pow (const complex<T>& x, const complex<T>& y);

template<class T> complex<T>

pow (T x, const complex<T>& y);

Gibt x erhoben in die Potenz y zurück.

T

real() const;

Gibt den reellen Teil der komplexen Zahl zurück.

template<class T> T

real (const complex<T>& c);

Gibt den reellen Teil von c zurück.

template<class T> complex<T>

sin (const complex<T>& c);

Gibt den Sinus von c zurück.

template<class T> complex<T>

sinh (const complex<T>& c);

Gibt den hyperbolischen Sinus von c zurück.

template<class T> complex<T>

sqrt (const complex<T>& x);

Gibt die Quadratwurzel von x zurück.

template<class T> complex<T>

tan (const complex<T>& x);

Gibt den Tangens von x zurück.

template<class T> complex<T>

tanh (const complex<T>& x);

Gibt den hyperbolischen Tangens von x zurück.


construct

Siehe auch	Primitiv zur Speicherverwaltung

Zeigerbasiertes Primitiv zur Speicherinitialisierung.

Syntax

#include <memory>

template <class T1, class T2>

 void construct (T1 *p, const T2& value)

Beschreibung

Die Template-Funktion construct initialisiert den Speicherort p bis value.


Container

Eine Kollektion der Standardbibliothek (Standard Template Library–STL).

Beschreibung

In der Standardbibliothek werden Kollektionsklassen häufig als Container beschrieben. Ein Container speichert eine Kollektion von Objekten und stellt bestimmte Grundfunktionen zur Verfügung, die die Verwendung generischer Algorithmen unterstützen. Container bestehen aus zwei Basistypen: Sequenzen und assoziativen Containern. Außerdem unterscheiden sie sich durch den Iteratorentyp, den sie unterstützen.

Eine Sequenz unterstützt eine lineare Anordnung einzelner Elemente. vector, list, deque und string fallen in diese Kategorie. Assoziative Container bilden Werte auf Indizes ab und bieten damit eine effektive Möglichkeit, diese Werte über die Indizes zu ermitteln. Die STL stellt die assoziativen Container map, multimap, set und multiset zur Verfügung. map und multimap speichern den Wert getrennt vom Index und ermöglichen so eine schnelle Ermittlung des Werts durch schnelle Ermittlung des Index. set und multiset speichern nur Indizes und ermöglichen eine schnelle Ermittlung der Indizes selbst.

Container-Anforderungen

Container in der STL müssen die folgenden Anforderungen erfüllen:

Ein Container weist den gesamten Speicher für die Objekte zu, die er enthält.

Ein Container X von Objekten vom Typ T stellt die folgenden Typen zur Verfügung:

X::value_type	ein T
X::reference	lvalue von T
X::const_reference	const lvalue von T
X::iterator	ein Iteratorentyp, der auf T zeigt. X::iterator kann kein Ausgabeiterator sein.
X::const_iterator	ein Iteratorentyp, der auf const T zeigt. Es kann sich um einen beliebigen Iteratorentyp mit Ausnahme eines Ausgabeiterators handeln.
X::difference_type	ein Integertyp mit Vorzeichen (muß der gleiche Typ wie der Differenztyp für X::iterator und X::const_iterator sein.
X::size_type	ein vorzeichenloser Integertyp, der einen nicht negativen Wert vom Typ of difference_type darstellt.
Ein Container besitzt einen Standardkonstruktor, einen Kopierkonstruktor, einen Zuweisungsoperator und einen kompletten Satz von Vergleichsoperatoren.

Ein Container besitzt die folgenden Elementfunktionen:

begin()	gibt einen Iterator zurück, der auf das erste Element der Kollektion zeigt
end()	gibt einen Iterator zurück, der genau hinter das letzte Element der Kollektion zeigt
swap(container)	vertauscht die Elemente dieses Containers mit dem Argument der Vertauschoperation
size()	gibt die Anzahl der Elemente in der Kollektion als size_type zurück
max_size()	gibt die größtmögliche Anzahl der Elemente für diesen Containertyp als size_type zurück
empty()	gibt true zurück, wenn der Container leer ist und false, wenn er nicht leer ist
Umkehrbare Container

Ein Container kann umkehrbar sein. Im wesentlichen besitzt ein umkehrbarer Container einen Reverse-Iterator, der das Durchlaufen der Kollektion in umgekehrter Richtung des Standarditerators ermöglicht. Ein umkehrbarer Container muß zusätzlich zu den oben ausgeführten Bedingungen folgende Anforderungen erfüllen:

Ein umkehrbarer Container stellt die folgenden Typen zur Verfügung:

X::reverse_iterator	ein Iteratorentyp, der auf T zeigt
X::const_reverse_iterator	ein Iteratorentyp, der auf T zeigt
Ein umkehrbarer Container stellt die folgenden Elementfunktionen zur Verfügung:

rbegin()	gibt einen reverse_iterator zurück, der hinter das Ende der Kollektion zeigt
rend()	gibt einen reverse_iterator zurück, der auf das erste Element der Kollektion zeigt
Sequenzen

Zusätzlich zu den Anforderungen an Container gelten die folgenden Anforderungen an Sequenzen:

iterator und const_iterator müssen Vorwärtsiteratoren, bidirektionale Iteratoren oder Random Access-Iteratoren sein.

Eine Sequenz stellt die folgenden Konstruktoren zur Verfügung:

X(n, t)	erzeugt einen Container mit n Elementen t
X(i, j)	erzeugt einen Container mit Elementen aus dem Bereich [i,j)
Eine Sequenz muß die folgenden Elementfunktionen zur Verfügung stellen:

insert(p,t)	fügt das Element t vor der durch den Iterator p bezeichneten Position ein
insert(p,n,t)	fügt n Elemente t vor der durch den Iterator p bezeichneten Position ein
insert(p,i,j)	fügt Elemente aus dem Bereich [i,j) vor der durch den Iterator p bezeichneten Position ein
erase(q)	löscht das Element, auf das der Iterator q zeigt
erase(q1,q2)	löscht die Elemente im Bereich [q1,q2)
Eine Sequenz kann auch die folgenden Elementfunktionen zur Verfügung stellen, wenn sie mit einer Komplexität implementiert werden können, die konstante Ausführungszeit erlaubt.

front()	gibt das Element zurück, auf das begin() zeigt
back()	gibt das Element zurück, auf das end() zeigt
push_front(x)	fügt das Element x bei begin() ein
push_back(x)	fügt das Element x bei end() ein
pop_front()	löscht das Element bei begin()
pop_back()	löscht das Element bei end() -1
operator[](n)	gibt das Element bei a.begin() + n zurück
Assoziative Container

Zusätzlich zu den Anforderungen an Container gelten die folgenden Anforderungen an assoziative Container:

Bei assoziativen Containern müssen iterator und const_iterator bidirectional_iterators sein. Assoziative Container sind von Natur aus sortiert. Ihre Iteratoren bewegen sich in der nicht absteigenden Reihenfolge der Indizes durch den Container (wobei nicht absteigende Reihenfolge durch das Vergleichsobjekt definiert ist, das zum Erzeugen des Containers verwendet wurde).

Ein assoziativer Container stellt die folgenden Typen zur Verfügung:

X::key_type	der Typ des Key
X::key_compare	der Typ des Vergleichs, der verwendet wird, um die Indizes in die richtige Reihenfolge zu bringen
X::value_compare	der Typ des Vergleichs, der mit Werten verwendet wird
Der Standardkonstruktor und der Kopierkonstruktor für assoziative Container verwenden die Klasse Template-Parameter-Vergleich.

Ein assoziativer Container stellt die folgenden zusätzlichen Konstruktoren zur Verfügung:

X(c)	erzeugt einen leeren Container und verwendet dabei c als Vergleichsobjekt
X(i,j,c)	erzeugt einen Container mit Elementen aus dem Bereich [i,j) und dem Vergleichsobjekt c
X(i, j)	erzeugt einen Container mit Elementen aus dem Bereich [i,j) unter Verwendung des Template-Parameter-Vergleichsobjekts
Ein assoziativer Container stellt die folgenden Elementfunktionen zur Verfügung:

key_comp()	Diese Funktion gibt das Vergleichsobjekt zurück, das beim Erzeugen des assoziativen Containers verwendet wurde.
value_comp()	Diese Funktion gibt das Wertvergleichsobjekt zurück, das beim Erzeugen des assoziativen Containers verwendet wurde.
a_uniq.insert(t)	Diese Funktion fügt t ein, wenn und nur wenn der Container kein Element mit einem Index gleich dem Index von t besitzt. Gibt pair<iterator,bool> zurück. Die Komponente bool des zurückgegebenen Paars gibt den Erfolg oder den Mißerfolg der Operation an und die Komponente iterator zeigt auf das Element, dessen Index gleich dem Index von t ist.
insert(t)	Diese Funktion fügt das Element t ein und gibt einen Iterator zurück, der auf das neu eingefügte Element zeigt.
insert(p,t)	Wenn der Container redundante Indexwerte nicht unterstützt, fügt diese Funktion nur dann t ein, wenn kein Index vorhanden ist, der gleich dem Index von t ist. Wenn der Container redundante Indizes unterstützt, fügt diese Funktion das Element t immer ein. Der Iterator p dient als Hinweis, wo mit der Suche begonnen wird und ermöglicht eine gewisse Optimierung des Einfügens. Er verhindert nicht, daß der Algorithmus bei Bedarf etwas vor dieser Position einfügt.
insert(i,j)	Diese Funktion fügt Elemente aus dem Bereich [i,j) ein.
erase(k)	Diese Funktion löscht alle Elemente mit einem Index gleich k.
erase(q1,q2)	Diese Funktion löscht die Elemente im Bereich [q1,q2).
find(k)	Diese Funktion gibt einen Iterator zurück, der auf ein Element mit einem Index gleich k oder end() zeigt, wenn ein solches Element nicht gefunden wurde.
count(k)	Diese Funktion gibt die Anzahl der Elemente mit einem Index gleich k zurück.
lower_bound(k)	Diese Funktion gibt einen Iterator zurück, der auf das erste Element mit einem Index nicht kleiner als k zeigt.
upper_bound(k)	Diese Funktion gibt einen Iterator zurück, der auf das erste Element mit einem Index größer als k zeigt.
equal_range(k)	Diese Funktion gibt ein Iteratorenpaar zurück, bei dem das erste Element des Paars äquivalent mit lower_bound(k) und das zweite Element äquivalent mit upper_bound(k) ist.


copy, copy_backward

Algorithmus

Kopiert einen Bereich von Elementen.

Syntax

#include <algorithm>

template <class InputIterator, class OutputIterator>

  OutputIterator

  copy(InputIterator first, InputIterator last,

       OutputIterator result);

template <class InputIterator, class OutputIterator>

  OutputIterator

  copy_backward(InputIterator first, InputIterator last,

                OutputIterator result);

Beschreibung

Der Algorithmus copy kopiert Werte aus dem durch [first ,last) angegebenen Bereich in den durch [result,result + (last - first)) angegebenen Bereich. copy kann verwendet werden, um Werte aus einem Container in einen anderen zu kopieren oder um Werte von einer Position in einem Container an eine andere Position desselben Containers zu kopieren, wenn result nicht im Bereich [first-last) liegt. copy gibt result + (last - first) zurück. Für jeden nicht negativen Integerwert n < (last - first) weist copy *(first + n) *(result + n) zu. Das Ergebnis von copy ist nicht definiert, wenn result im Bereich [first, last) liegt.

Wenn result kein Einfügeiterator ist, geht copy davon aus, daß mindestens ebenso viele Elemente auf result folgen, wie sich Elemente im Bereich [first, last) befinden.

Der Algorithmus copy_backward kopiert Elemente aus dem durch [first, last) angegebenen Bereich in den durch [result - (last - first), result) angegebenen Bereich, beginnend vom Ende der Sequenz (last-1) und an den Anfang (first) fortschreitend. Beachten Sie, daß copy_backward die Reihenfolge der Elemente nicht umkehrt. Der Algorithmus kehrt nur die Reihenfolge der Übertragung um. copy_backward gibt result - (last - first) zurück. Sie sollten copy_backward statt copy verwenden, wenn last im Bereich [result - (last - first), result) liegt. Für jeden positiven Integerwert n <= (last - first) weist copy_backward *(last - n) *(result - n) zu. Das Ergebnis von copy_backward ist nicht definiert, wenn result im Bereich [first, last) liegt.

Wenn result kein Einfügeiterator ist, geht copy_backward davon aus, daß mindestens ebenso viele Elemente vor result liegen, wie sich Elemente im Bereich [first, last) befinden.

Sowohl copy als auch copy_backward führen genau last - first Zuweisungen aus.

Beispiel

 #include <algorithm>

 #include <vector>

 using namespace std;

 int main()

 {

   int d1[4] = {1,2,3,4};

   int d2[4] = {5,6,7,8};

   // Erzeugt drei Vektoren

   vector<int> v1(d1,d1 + 4), v2(d2,d2 + 4), v3(d2,d2 + 4);

   // Erzeugt einen leeren Vektor

   vector<int> v4;

   // Kopiert v1 nach v2

   copy(v1.begin(),v1.end(),v2.begin());

   // Kopiert v1 rückwärts nach v3

   copy_backward(v1.begin(),v1.end(),v3.end());

   // Verwendet Einfügeoperator, um in den leeren Vektor zu kopieren

   copy(v1.begin(),v1.end(),back_inserter(v4));

   // Kopiert alle vier nach cout

   ostream_iterator<int> out(cout," ");

   copy(v1.begin(),v1.end(),out);

   cout << endl;

   copy(v2.begin(),v2.end(),out);

   cout << endl;

   copy(v3.begin(),v3.end(),out);

   cout << endl;

   copy(v4.begin(),v4.end(),out);

   cout << endl;

   return 0;

 }


count, count_if

Algorithmus

Zählt die Elemente in einem Container, die einer gegebenen Bedingung genügen.

Syntax

#include <algorithm>

template <class InputIterator, class T, class Size>

  void

  count(InputIterator first, InputIterator last,

        const T& value, Size& n);

template <class InputIterator, class Predicate, class Size>

  void

  count_if(InputIterator first, InputIterator last,

           Predicate pred, Size& n);

Beschreibung

Der Algorithmus count vergleicht value mit Elementen in der durch die Iteratoren first und last definierten Sequenz und inkrementiert einen Zählwert n, wenn eine Entsprechung gefunden wird. count addiert also zu n die Anzahl der Iteratoren i im Bereich [first, last), für die die folgende Bedingung gilt:

*i == value

Der Algorithmus count_if erlaubt die Angabe eines Prädikats und inkrementiert n immer, wenn ein Element in der Sequenz dem Prädikat genügt. count_if addiert also zu n die Anzahl der Iteratoren i im Bereich [first, last), für die die folgende Bedingung gilt:

pred(*i) == true.

Sowohl count als auch count_if führen genau last-first Anwendungen des entsprechenden Prädikats durch.

Beispiel

 #include <vector>

 #include <algorithm>

 using namespace std;

 int main()

 {

   int sequence[10] = {1,2,3,4,5,5,7,8,9,10};

   int i=0,j=0,k=0;

   // Erzeugen eines Vektors

   vector<int> v(sequence,sequence + 10);

   count(v.begin(),v.end(),5,i);	// Fünfen zählen

   count(v.begin(),v.end(),6,j);	// Sechsen zählen

   // i = 2, j = 0

   // Alle Werte kleiner 8 zählen

   count_if(v.begin(),v.end(),bind2nd(less<int>(),8),k);

   // k = 7

   cout << i << " " << j << " " << k << endl;

   return 0;

 }


deallocate

Siehe auch	Primitiv zur Speicherverwaltung

Ein zeigerbasiertes Primitiv zur Speicherverwaltung.

Syntax

#include <memory>

template <class T>

 void deallocate (T* buffer);

Beschreibung

Die Template-Funktion deallocate gibt den durch buffer belegten Speicher zur systemweiten Verwendung frei.


deque

Container

Eine Sequenz, die Random Access-Iteratoren und effektives Einfügen/Löschen sowohl am Anfang als auch am Ende unterstützt.

Syntax

#include <deque>

template <class T>

 class deque {

public:

 // Typen

   typedef typename reference;

   typedef typename const_reference;

   typedef typename iterator;

   typedef typename const_iterator;

   typedef typename size_type;

   typedef typename difference_type;

   typedef T value_type;

   typedef reverse_iterator<iterator,

           value_type, reference,

           difference_type> reverse_iterator;

   typedef reverse_iterator<const_iterator,

           value_type, const_reference,

           difference_type> const_reverse_iterator;

 // Erzeugen/Kopieren/Entfernen

   explicit deque ();

   explicit deque (size_type, const T& = T());

   deque (const deque<T>&);

   template <class InputIterator>

    deque (InputIterator, InputIterator);

   ~deque ();

   deque<T> operator= (const deque<T>);

   template <class InputIterator>

    assign (InputIterator, InputIterator);

   template <class Size, class T>

    void assign (Size n, const T& t = T());

 // Iteratoren

   iterator begin ();

   const_iterator begin () const;

   iterator end ();

   const_iterator end () const;

   reverse_iterator rbegin ();

   const_reverse_iterator rbegin () const;

   reverse_iterator rend ();

   const_reverse_iterator rend () const;

// Kapazität

   size_type size () const;

   size_type max_size () const;

   void resize (size_type,T c = T());

   bool empty () const;

// Elementzugriff

   reference operator[] (size_type);

   const_reference operator[] (size_type) const;

   reference at (size_type n);

   const_reference at (size_type n) const;

   reference front ();

   const_reference front () const;

   reference back ();

   const_reference back () const;

 // Modifizierer

   void push_front (const T&);

   void push_back (const T&);

   iterator insert (iterator, const T& = T());

   void insert (iterator, size_type, const T& = T());

   template <class InputIterator>

    void insert (iterator, InputIterator, InputIterator);

   void pop_front ();

   void pop_back ();

   void erase (iterator);

   void erase (iterator, iterator);

   void swap (deque<T>&);

};

 // Vergleich

template <class T>

 bool operator== (const deque<T>&, const deque <T>&);

template <class T>

 bool operator< (const deque<T>, const deque <T>&);

Beschreibung

deque<T> ist ein Sequenztyp, der Random Access-Iteratoren unterstützt. Außerdem werden Einfüge- und Löschoperationen mit konstanter Ausführungszeit am Anfang und am Ende des Containers unterstützt. Einfügen und Löschen in der Mitte des Containers finden in linearer Ausführungszeit statt. Die Speicherverwaltung erfolgt automatisch.

Alle Typen, die für den Template-Parameter T verwendet werden, müssen folgendes zur Verfügung stellen (wobei T der type ist, t ein value von T ist und u ein const value von T ist):

Standardkonstruktor	T()

Kopierkonstruktoren	T(t) und T(u)

Destruktor	t.~T()

Adresse von	&t und &u, die T* bzw. const T* zurückgeben

Zuweisung	t = a, wobei a ein (möglicherweise const) value von T ist

Beschränkungen

Elementfunktion-Templates werden in allen Containern verwendet, die die Standardbibliothek zur Verfügung stellt. Ein Beispiel hierfür ist der Konstruktor für deque<T>, der zwei Template-Iteratoren annimmt:

template <class InputIterator>

 deque (InputIterator, InputIterator);

deque besitzt außerdem eine Einfügefunktion dieses Typs. Mit diesen Funktionen können Sie - wenn keine Compiler-Einschränkungen vorliegen - jeden Typ von Eingabiterator als Argument verwenden. Für Compiler, die diese Funktionen nicht unterstützen, gibt es Ersatzfunktionen, die es Ihnen ermöglichen, einen Iterator zu verwenden, der vom gleichen Containertyp stammt wie der zu konstruierende (bzw. für den eine Elementfunktion aufgerufen wird). Sie können auch mit einem Zeiger auf den Elementtyp arbeiten, den der Container enthält.

Wenn Ihr Compiler keine Elementfunktion-Templates unterstützt, können Sie ein deque zum Beispiel auf die folgenden beiden Arten konstruieren:

int intarray[10];

deque<int> first_deque(intarray,intarray + 10);

deque<int>

second_deque(first_deque.begin(),first_deque.end());

Dies ist jedoch nicht möglich:

deque<long>

long_deque(first_deque.begin(),first_deque.end());

da long_deque und first_deque nicht vom gleichen Typ sind.

Beispiel

 #include <deque>

 #include <string>

 using namespace std;

 deque<string> deck_of_cards;

 deque<string> current_hand;

 void initialize_cards(deque<string>& cards) {

   cards.push_front("aceofspades");

   cards.push_front("kingofspades");

   cards.push_front("queenofspades");

   cards.push_front("jackofspades");

   cards.push_front("tenofspades");

   // etc.

 }

 template <class It, class It2>

 void print_current_hand(It start, It2 end)

 {

   while (start < end)

   cout << *start++ << endl;

 }

 template <class It, class It2>

 void deal_cards(It, It2 end) {

   for (int i=0;i<5;i++) {

     current_hand.insert(current_hand.begin(),*end);

     deck_of_cards.erase(end++);

   }

 }

 void play_poker() {

   initialize_cards(deck_of_cards);

   deal_cards(current_hand.begin(),deck_of_cards.begin());

 }

 int main()

 {

   play_poker();

   print_current_hand(current_hand.begin(),current_hand.end());

   return 0;

 }

Konstruktoren und Destruktoren

explicit

deque ();

Der Standardkonstruktor. Erzeugt ein Deque mit null Elementen.

explicit

deque (size_type n, const T& value = T());

Erzeugt ein Deque der Länge n, der n Kopien von value enthält.

deque (const deque<T>& x);

Kopierkonstruktor. Erzeugt eine Kopie von x.

template <class InputIterator>

deque (InputIterator first, InputIterator last);

Erzeugt ein Deque der Länge last - first, der mit allen Werten gefüllt ist, die sich durch das Dereferenzieren des InputIterators im Bereich [first, last) ergeben.

~deque ();

Der Destruktor. Gibt den für self alloziierten Speicher frei.

Iteratoren

iterator begin ();

Gibt einen Random Access-Iterator zurück, der auf das erste Element zeigt.

const_iterator begin () const;

Gibt einen konstanten Random Access-Iterator zurück, der auf das erste Element zeigt.

iterator end ();

Gibt einen Random Access-Iterator zurück, der auf den Nach-Bereichsende-Wert zeigt.

const_iterator end () const;

Gibt einen konstanten Random Access-Iterator zurück, der auf den Nach-Bereichsende-Wert zeigt.

reverse_iterator rbegin ();

Gibt einen Random Access-Iterator zurück, der auf den Nach-Bereichsende-Wert zeigt.

const_reverse_iterator rbegin () const;

Gibt einen konstanten Random Access-Iterator zurück, der auf den Nach-Bereichsende-Wert zeigt.

reverse_iterator rend ();

Gibt einen Random Access-Iterator zurück, der auf das erste Element zeigt.

const_reverse_iterator rend () const;

Gibt einen konstanten Random Access-Iterator zurück, der auf das erste Element zeigt.

Zuweisungsoperator

deque<T>&

operator= (const deque<T>& x);

Zuweisungsoperator. Löscht alle Elemente in self und fügt anschließend in self eine Kopie jedes Elements in x ein. Gibt eine Referenz auf self zurück.

Referenzoperatoren

reference operator[] (size_type n);

Gibt eine Referenz auf Element n von self zurück. Das Ergebnis kann als lvalue verwendet werden. Der Index n muß zwischen 0 und der Größe minus Eins liegen.

const_reference operator[] (size_type) const;

Gibt eine konstante Referenz auf Element n von self zurück. Der Index n muß zwischen 0 und der Größe minus Eins liegen.

Vergleichsoperatoren

template <class T>

bool

operator== (const deque<T>& x, const deque T>& y);

Gleichheitsoperator. Gibt true zurück, wenn x gleich y ist.

template <class T>

bool

operator< (const deque<T>& x, const deque T>& y);

Gibt true zurück, wenn die in x enthaltenen Elemente lexikographisch kleiner als die in y enthaltenen Elemente sind.

Elementfunktionen

template <class InputIterator>

void

assign (InputIterator first, InputIterator last);

Löscht alle in self enthaltenen Elemente und fügt anschließend neue Elemente aus dem Bereich [first, last) ein.

template <class Size, class T>

void

assign (Size n, const T& t = T());

Löscht alle in self enthaltenen Elemente und fügt anschließend n Instanzen des Werts von t ein.

reference

at (size_type n);

Gibt eine Referenz auf Element n von self zurück. Das Ergebnis kann als lvalue verwendet werden. Der Index n muß zwischen 0 und der Größe minus Eins liegen.

const_reference

at (size_type) const;

Gibt eine konstante Referenz auf Element n von self zurück. Der Index n muß zwischen 0 und der Größe minus Eins liegen.

reference

back ();

Gibt eine Referenz auf das letzte Element zurück.

const_reference

back () const;

Gibt eine konstante Referenz auf das letzte Element zurück.

bool

empty () const;

Gibt true zurück, wenn size von self Null ist.

void

erase (iterator position);

Entfernt das Element, auf das position zeigt.

void

erase (iterator first, iterator last);

Entfernt die Elemente im Bereich [first, last).

reference

front ();

Gibt eine Referenz auf das erste Element zurück.

const_reference

front () const;

Gibt eine konstante Referenz auf das erste Element zurück.

iterator

insert (iterator position, const T& x = T());

Fügt x vor position ein. Der Rückgabewert zeigt auf das eingefügte x.

void

insert (iterator position, size_type n, const T& x =T());

Fügt n Kopien von x vor position ein.

template <class InputIterator>

void

insert (iterator position, InputIterator first,

        InputIterator last);

Fügt Kopien der Elemente im Bereich (first, last] vor position ein.

size_type

max_size () const;

Gibt size () des größten möglichen Deques zurück.

void

pop_back ();

Entfernt das letzte Element. Beachten Sie, daß diese Funktion das Element nicht zurückgibt.

void

pop_front ();

Entfernt das erste Element. Beachten Sie, daß diese Funktion das Element nicht zurückgibt.

push_back (const T& x);

Hängt eine Kopie von x am Ende an.

void

push_front (const T& x);

Fügt eine Kopie von x am Anfang ein.

void

resize (size_type sz, T c = T());

Ändert die Größe von self . Wenn die neue Größe (sz) größer als die aktuelle Größe ist, werden sz-size() cs am Ende des Deques eingefügt. Wenn die neue Größe kleiner als die aktuelle Kapazität ist, wird der Deque abgeschnitten, indem size()-sz Elemente am Ende entfernt werden. Wenn sz gleich capacity ist, findet keine Aktion statt.

size_type

size () const;

Gibt die Anzahl der Elemente zurück.

void

swap (deque<T>& x);

Vertauscht self und x.


destroy

Siehe auch	Primitiv zur Speicherverwaltung

Ruft den Destruktor für Werte auf, auf die Iteratoren oder Zeiger zeigen.

Syntax

#include <memory>

template <class ForwardIterator>

void destroy (ForwardIterator first, ForwardIterator last)

template <class T>

void destroy (T* pointer)

Beschreibung

template <class T>

void

destroy (T* pointer)

Ruft den Destruktor für den Wert auf, auf den der Argumentzeiger zeigt.

template <class ForwardIterator>

void

destroy (ForwardIterator first, ForwardIterator last)

Entfernt alle Werte im Bereich [first, last).


distance

Siehe auch	Iteratoroperation

Berechnet den Abstand zwischen zwei Iteratoren.

Syntax

#include <iterator>

template <class InputIterator, class Distance>

 void distance (InputIterator first,

                InputIterator last,

                Distance& n);

Beschreibung

Die Template-Funktion distance berechnet den Abstand zwischen zwei Iteratoren und speichert den Wert in n. Der letzte Iterator muß vom ersten Iterator aus erreichbar sein.

distance inkrementiert n um die Zahl der Durchgänge, die benötigt werden, um von first nach last zu gelangen. distance muß eine Funktion mit drei Argumenten sein, die das Ergebnis in einer Referenz speichert, statt das Ergebnis zurückzugeben, da der Typ distance nicht aus eingebauten Iteratorentypen wie int* abgeleitet werden kann.

Beispiel

 #include<iterator>

 #include<vector>

 using namespace std;

 int main()

 {

   //Initialisieren eines Vektors mit einem Array

   int arr[6] = {3,4,5,6,7,8};

   vector<int> v(arr,arr+6);

   //Deklarieren eines Listeniterators, z.B. eines ForwardIterator

   vector<int>::iterator itr = v.begin()+3;

   //Ausgabe des Originalvektors

   cout << "For the vector: ";

   copy(v.begin(),v.end(),ostream_iterator<int>(cout," "));

   cout << endl << endl;

   cout << "When the iterator is initialized to point to "

        << *itr << endl << endl;

   // distance verwenden

   vector<int>::difference_type dist = 0;

   distance(v.begin(), itr, dist);

   cout << "The distance between the beginning and itr is "

        << dist << endl;

   return 0;

 }


divides

Funktionsobjekt

Gibt das Ergebnis der Division des ersten Arguments durch das zweite zurück.

Syntax

#include <functional>

template <class T>

   struct divides : binary_function<T, T, T> {

        T operator() (const T& x, const T& y) const

              { return x / y; }

   };

Beschreibung

divides ist ein binäres Funktionsobjekt. Sein operator() gibt das Ergebnis der Division von x durch y zurück. Sie können ein Objekt divides an jeden Algorithmus übergeben, der eine binäre Funktion benötigt. Beispielsweise wendet der Algorithmus transform eine binäre Operation auf die entsprechenden Werte in zwei Kollektionen an und speichert das Ergebnis. divides wird in diesem Algorithmus auf die folgende Weise verwendet:

vector<int> vec1;

vector<int> vec2;

vector<int> vecResult;

.

.

.

transform(vec1.begin(), vec1.end(),
          vec2.begin(), vec2.end(),
          vecResult.begin(), divides<int>());

Nach diesem Aufruf von transform enthält vecResult[n] vec1[n] dividiert durch vec2[n].


equal

Algorithmus

Vergleicht zwei Bereiche auf Äquivalenz.

Syntax

#include <algorithm>

template <class InputIterator1, class InputIterator2>

  bool

  equal(InputIterator1 first1, InputIterator1 last1,

        InputIterator2 first2);

template <class InputIterator1, class InputIterator2,

          class BinaryPredicate>

  bool

  equal(InputIterator1 first1, InputIterator1 last1,

        InputIterator2 first2, BinaryPredicate binary_pred);

Beschreibung

Der Algorithmus equal führt einen paarweisen Vergleich aller Elemente in einem Bereich mit allen Elementen eines anderen Bereichs durch und untersucht, ob sie sich entsprechen. Die erste Version von equal verwendet den Gleichheitsoperator (==) als Vergleichsfunktion. Die zweite Version ermöglicht die Angabe eines binären Prädikats als Vergleichsfunktion. Die erste Version gibt true zurück, wenn alle entsprechenden Elemente gleich sind. Die zweite Version von equal gibt true zurück, wenn für jedes Elementpaar in den beiden Bereichen das Ergebnis der Anwendung des binären Prädikats true ist. Anders gesagt, gibt equal true zurück, wenn die beiden folgenden Aussagen wahr sind:

1.	Der zweite Bereich enthält mindestens ebenso viele Elemente wie der erste Bereich.

2.	Für jeden Iterator i im Bereich [first1, last1) gelten die entsprechenden folgenden Bedingungen:

*i == *(first2 + (i - first1))

oder

binary_pred(*i, *(first2 + (i - first1))) == true

Andernfalls gibt equal false zurück.

Dieser Algorithmus geht davon aus, daß nach first2 mindestens ebenso viele Elemente verfügbar sind wie der Bereich [first1, last1) enthält.

equal führt höchstens last1-first1 Vergleiche oder Anwendungen des Prädikats durch.

Beispiel

 #include <algorithm>

 #include <vector>

 using namespace std;

 int main()

 {

   int d1[4] = {1,2,3,4};

   int d2[4] = {1,2,4,3};

   // Erzeugen zweier Vektoren

   vector<int> v1(d1,d1 + 4), v2(d2,d2 + 4);

   // Gleichheit überprüfen

   bool b1 = equal(v1.begin(),v1.end(),v2.begin());

   bool b2 = equal(v1.begin(),v1.end(),

                   v2.begin(),equal_to<int>());

   // Sowohl b1 als auch b2 sind false

   cout << (b1 ? "TRUE" : "FALSE")  << " "

        << (b2 ? "TRUE" : "FALSE") << endl;

   return 0;

 }


equal_range

Algorithmus

Bestimmt den gültigen Bereich für das Einfügen eines Werts in einen Container.

Syntax

#include <algorithm>

template <class ForwardIterator, class T>

    pair<ForwardIterator, ForwardIterator>

    equal_range(ForwardIterator first, ForwardIterator last,

                const T& value);

 template <class ForwardIterator, class T, class Compare>

    pair<ForwardIterator, ForwardIterator>

    equal_range(ForwardIterator first, ForwardIterator last,

                const T& value, Compare comp);

Beschreibung

Der Algorithmus equal_range führt eine binäre Suche in einem geordneten Container durch, um festzustellen, wo das Element value eingefügt werden kann, ohne die Ordnung des Containers zu zerstören. Die Bibliothek stellt zwei Versionen des Algorithmus zur Verfügung. Die erste Version verwendet den Operator kleiner als (operator <), um den gültigen Einfügebereich zu suchen und geht davon aus, daß die Sequenz mit dem Operator kleiner als sortiert wurde. Die zweite Version erlaubt die Angabe eines Funktionsobjekts vom Typ compare und geht davon aus, daß compare die zum Sortieren der Sequenz verwendete Funktion war. Das Funktionsobjekt muß ein binäres Prädikat sein.

equal_range gibt ein Iteratorenpaar i und j zurück, das einen Bereich definiert, der Elemente enthält, die äquivalent mit value sind, d.h. den ersten und den letzten gültigen Einfügepunkt für value. Wenn value kein Element des Containers ist, sind i und j gleich. Andernfalls zeigt i auf das erste Element, das nicht "kleiner" als value ist, und j zeigt auf das erste Element, das größer als value ist. In der zweiten Version wird "kleiner" durch das Vergleichsobjekt definiert. Formal gibt equal_range einen Unterbereich [i, j) zurück, in den value bei jedem Iterator k innerhalb des Bereichs eingefügt werden kann. Abhängig von der verwendeten Version des Algorithmus muß k einer der folgenden Bedingungen genügen:

!(*k <  value)  &&  !(value  <  *k)

oder

comp(*k,value) == false && comp(value, *k) == false

equal_range führt höchstens 2 * log(last - first) + 1 Vergleiche durch.

Beispiel

 #include <vector>

 #include <algorithm>

 using namespace std;

 int main()

 {

   typedef vector<int>::iterator iterator;

int d1[11] = {0,1,2,2,3,4,2,2,2,6,7};

// Erzeugen eines Vektors

   vector<int> v1(d1,d1 + 11);

   // Varianten von equal_range versuchen

   pair<iterator,iterator> p1 =

       equal_range(v1.begin(),v1.end(),3);

   // p1 = (v1.begin() + 4,v1.begin() + 5)

   pair<iterator,iterator> p2 =

       equal_range(v1.begin(),v1.end(),2,less<int>());

   // p2 = (v1.begin() + 4,v1.begin() + 5)

   // Ausgabe der Ergebnisse

   cout << endl  << "The equal range for 3 is: "

        << "( " << *p1.first << " , "

        << *p1.second << " ) " << endl << endl;

   cout << endl << "The equal range for 2 is: "

        << "( " << *p2.first << " , "

        << *p2.second << " ) " << endl;

   return 0;

 }


equal_to

Funktionsobjekt

Binäres Funktionsobjekt, das true zurückgibt, wenn sein erstes Argument gleich seinem zweiten ist.

Syntax

#include <functional>

    template <class T>

    struct equal_to : binary_function<T, T, bool> {

         bool operator() (const T& x, const T& y) const

               { return x == y; }

    };

Beschreibung

equal_to ist ein binäres Funktionsobjekt. Sein operator() gibt true zurück, wenn x gleich y ist. Sie können ein Objekt equal_to an jeden Algorithmus übergeben, der ein binäres Funktionsobjekt benötigt. Der Algorithmus transform wendet zum Beispiel eine binäre Operation auf entsprechende Werte in zwei Kollektionen an und speichert das Ergebnis. equal_to wird in diesem Algorithmus auf die folgende Art verwendet:

vector<int> vec1;

vector<int> vec2;

vector<int> vecResult;

.

.

.

transform(vec1.begin(), vec1.end(),
          vec2.begin(), vec2.end(),
          vecResult.begin(), equal_to<int>());

Nach diesem Aufruf von transform enthält vecResult(n) eine "1", wenn vec1(n) gleich vec2(n) war oder eine "0", wenn vec1(n) ungleich vec2(n) war.


exception

Standard-Exception

Klassen zur Behandlung logischer Fehler und Laufzeitfehler.

Syntax

 #include <stdexcept>

 class exception {

  public:

   exception () throw();

   exception (const exception&) throw();

   exception& operator= (const exception&) throw();

   virtual ~exception () throw();

   virtual const char* what () const throw();

 };

 class logic_error : public exception {

  public:

   logic_error (const string& what_arg);

 };

 class domain_error : public logic_error {

  public:

   domain_error (const string& what_arg);

 };

 class invalid_argument : public logic_error {

  public:

   invalid_argument (const string& what_arg);

 };

 class length_error : public logic_error {

  public:

   length_error (const string& what_arg);

 };

 class out_of_range : public logic_error {

  public:

   out_of_range (const string& what_arg);

 };

 class runtime_error : public exception {

  public:

   runtime_error (const string& what_arg);

 };

 class range_error : public runtime_error {

  public:

   range_error (const string& what_arg);

 };

 class overflow_error : public runtime_error {

  public:

   overflow_error (const string& what_arg);

 };

Beschreibung

Die Klasse exception definiert die Basisklasse für die Objekttypen, die von Komponenten der C++ Standardbibiliothek als Exceptions ausgelöst werden sowie bestimmte Ausdrücke, die bei der Programmausführung entdeckte Fehler melden. Anwender können diese Exceptions auch verwenden, um Fehler in Ihren eigenen Programmen zu melden.

Konstruktoren

exception () throw();

Konstruiert ein Objekt der Klasse exception.

exception (const exception&) throw();

Der Kopierkonstruktor. Kopiert ein Exception-Objekt.

Destruktor

virtual

~exception() throw();

Entfernt ein Objekt der Klasse exception.

Operatoren

exception&

operator= (const exception&) throw();

Der Zuweisungsoperator. Kopiert ein Exception-Objekt.

Elementfunktion

virtual const char*

what()const throw();

Gibt einen implementationsdefinierten, nullterminierten Byte-String zurück, der eine für den Anwender lesbare Botschaft darstellt, die die Exception beschreibt. Die Meldung kann ein nullterminierter Multibyte-String sein, der sich zur Umwandlung und Anzeige als wstring eignet.

Konstruktoren für abgeleitete Klassen

logic_error::logic_error (const string& what_arg);

Konstruiert ein Objekt der Klasse logic_error.

domain_error::domain_error (const string& what_arg);

Konstruiert ein Objekt der Klasse domain_error.

invalid_argument::invalid_argument (const string& what_arg);

Konstruiert ein Objekt der Klasse invalid_argument.

length_error::length_error (const string& what_arg);

Konstruiert ein Objekt der Klasse length_error.

out_of_range::out_of_range (const string& what_arg);

Konstruiert ein Objekt der Klasse out_of_range.

runtime_error::runtime_error (const string& what_arg);

Konstruiert ein Objekt der Klasse runtime_error.

range_error::range_error (const string& what_arg);

Konstruiert ein Objekt der Klasse range_error.

overflow_error::overflow_error (const string& what_arg);

Konstruiert ein Objekt der Klasse overflow_error.

Beispiel

 #include <iostream.h>

 #include <stdexcept>

 using namespace std;

 static void f() { throw runtime_error("a runtime error"); }

 int main ()

 {

   //

   // Indem das Hauptprogramm (main) in einen try-catch-Block eingerahmt

   // wird, wird sichergestellt, daß alle Exceptions in der Exception-

   // Hierarchie abgefangen werden. Sie können Exceptions einfach wie unten

   // gezeigt abfangen, oder Sie können jede Exception abfangen, die Sie
 // interessiert.

   //

   try

   {

     f();

   }

   catch (const exception& e)

   {

     cout << "Got an exception: " << e.what() << endl;

   }

   return 0;

 }


fill, fill_n

Algorithmus

Initialisiert einen Bereich mit einem gegebenen Wert.

Syntax

#include <algorithm>

template <class ForwardIterator, class T>

  void

  fill(ForwardIterator first, ForwardIterator last,

       const T& value);

template <class OutputIterator, class Size, class T>

  void fill_n(OutputIterator first, Size n, const T& value);

Beschreibung

Die Algorithmen fill und fill_n werden verwendet, um den Elementen einer Sequenz einen Wert zuzuweisen. fill weist den Wert allen Elementen zu, die durch die Iteratoren im Bereich [first, last) bestimmt werden.

Der Algorithmus fill_n weist den Wert allen Elementen zu, die durch die Iteratoren im Bereich [first, first + n) bestimmt werden. fill_n geht davon aus, daß mindestens n Elemente auf first folgen, es sei denn, first ist ein Einfügeiterator.

fill führt genau last - first Zuweisungen durch, und fill_n führt genau n Zuweisungen durch.

Beispiel

 #include <algorithm>

 #include <vector>

 using namespace std;

 int main()

 {

   int d1[4] = {1,2,3,4};

   // Erzeugen zweier Vektoren

   vector<int> v1(d1,d1 + 4), v2(d1,d1 + 4);

   // Erzeugen eines leeren Vektors

   vector<int> v3;

   // v1 ganz mit 9 füllen

   fill(v1.begin(),v1.end(),9);

   // Die ersten 3 Elemente von v2 mit 7 füllen

   fill_n(v2.begin(),3,7);

   // Einfügeiterator verwenden, um v3 mit 5 11en zu füllen

   fill_n(back_inserter(v3),5,11);

   // Alle drei nach cout kopieren

   ostream_iterator<int> out(cout," ");

   copy(v1.begin(),v1.end(),out);

   cout << endl;

   copy(v2.begin(),v2.end(),out);

   cout << endl;

   copy(v3.begin(),v3.end(),out);

   cout << endl;

   // cout mit 3 5en füllen

   fill_n(ostream_iterator<int>(cout," "),3,5);

   cout << endl;

   return 0;

 }


find

Siehe auch	Algorithmus

Sucht das Vorkommen eines Werts in einer Sequenz.

Syntax

#include <algorithm>

template <class InputIterator, class T>

  InputIterator

  find(InputIterator first, InputIterator last,

       const T& value);

Beschreibung

Der Algorithmus find sucht das erste Vorkommen eines bestimmten Werts in einer Sequenz. find gibt den ersten Iterator i im Bereich [first, last) zurück, für den die folgende Bedingung zutrifft:

*i == value.

Wenn find keine Entsprechung für value findet, wird der Iterator last zurückgegeben.

find führt höchstens last-first Vergleiche aus.

Beispiel

 #include <vector>

 #include <algorithm>

 using namespace std;

 int main()

 {

   typedef vector<int>::iterator iterator;

   int d1[10] = {0,1,2,2,3,4,2,2,6,7};

   // Erzeugen eines Vektors

   vector<int> v1(d1,d1 + 10);

   // find versuchen

   iterator it1 = find(v1.begin(),v1.end(),3);

   // it1 = v1.begin() + 4;

   // find_if versuchen

   iterator it2 =

      find_if(v1.begin(),v1.end(),bind1st(equal_to<int>(),3));

   // it2 = v1.begin() + 4

   // Beide Versionen von adjacent_find versuchen

   iterator it3 = adjacent_find(v1.begin(),v1.end());

   // it3 = v1.begin() +2

   iterator it4 =

      adjacent_find(v1.begin(),v1.end(),equal_to<int>());

   // v4 = v1.begin() + 2

   // Ausgabe der Ergebnisse

   cout << *it1 << " " << *it2 << " " << *it3 << " "

        << *it4 << endl;

   return 0;

 }


find_first_of

Siehe auch	Algorithmus

Sucht eine Entsprechung für eine gegebene Untersequenz in einer Sequenz.

Syntax

#include <algorithm>

template <class ForwardIterator1, class ForwardIterator2>

ForwardIterator1 find_first_of (ForwardIterator1 first1,

               ForwardIterator1 last1,

               ForwardIterator2 first2,

               ForwardIterator2 last2);

template <class ForwardIterator1, class ForwardIterator2,

          class BinaryPredicate>

ForwardIterator1 find_first_of (ForwardIterator1 first1,

               ForwardIterator1 last1,

               ForwardIterator2 first2,

               ForwardIterator2 last2,

               BinaryPredicate pred);

Beschreibung

Der Algorithmus find_first_of sucht eine durch first2,last2 angegebene Untersequenz in einer durch first1, last1 angegebenen Sequenz. Es gibt zwei Versionen des Algorithmus. Die erste Version verwendet den Gleichheitsoperator als binäres Standardprädikat; die zweite Version ermöglicht die Angabe eines binären Prädikats. Der Algorithmus gibt einen Iterator im Bereich [first1,last1)zurück, der auf das erste Element der passenden Untersequenz zeigt. Wird die Untersequenz in der Sequenz nicht gefunden, gibt find_first_of last1 zurück.

Anders ausgedrückt, liefert find_first_of einen Iterator i im Bereich [first1,last1) zurück, daß für mindestens ein j im Bereich [first2,last2) gilt:

 *i ==*(first2 + n)

oder

pred(i, first2+n)==true

find_first_of führt höchstens(last1 - first1) Anwendungen des entsprechenden Prädikats durch.

Beispiel

 #include <vector>

 #include <iterator>

 #include <algorithm>

 using namespace std;

 int main()

 {

   typedef vector<int>::iterator iterator;

   int d1[10] = {0,1,2,2,3,4,2,2,6,7};

   int d2[2] = {6,4};

   // Erzeugen zweier Vektoren

   vector<int> v1(d1,d1 + 10), v2(d2,d2 + 2);

   // Beide Versionen von find_first_of versuchen

   iterator it1 =

     find_first_of(v1.begin(),v1.end(),v2.begin(),v2.end());

   find_first_of(v1.begin(),v1.end(),v2.begin(),v2.end(),

                    equal_to<int>());

   // Ausgabe der Ergebnisse

   cout << "For the vectors: ";

   copy(v1.begin(),v1.end(),

        ostream_iterator<int>(cout," " ));

   cout << " and ";

   copy(v2.begin(),v2.end(),

        ostream_iterator<int>(cout," " ));

   cout << endl << endl

       << "both versions of find_first_of point to: "

       << *it1;

   return 0;

 }


find_if

Siehe auch	Algorithmus

Sucht das Vorkommen von value in einer Sequenz.

Syntax

#include <algorithm>

template <class InputIterator, class Predicate>

  InputIterator

  find_if(InputIterator first, InputIterator last,

          Predicate pred);

Beschreibung

Der Algorithmus find_if sucht nach dem ersten Element in einer Sequenz, das einer bestimmten Bedingung genügt. Die Sequenz wird durch die Iteratoren first und last definiert, während die Bedingung durch das dritte Argument von find_if definiert wird: eine Prädikatfunktion, die einen boolschen Wert zurückliefert. find_if gibt den ersten Iterator i im Bereich [first, last) zurück, für den die folgende Bedingung erfüllt ist:

 pred(*i) == true.

Wird kein solcher Iterator gefunden, gibt find_if last zurück.

find_if führt höchstens last-first Anwendungen des entsprechenden Prädikats durch.

Beispiel

 #include <vector>

 #include <algorithm>

 using namespace std;

 int main()

 {

   typedef vector<int>::iterator iterator;

   int d1[10] = {0,1,2,2,3,4,2,2,6,7};

   // Erzeugen eines Vektors

   vector<int> v1(d1,d1 + 10);

   // find versuchen

   iterator it1 = find(v1.begin(),v1.end(),3);

   // it1 = v1.begin() + 4;

   // find_if versuchen

   iterator it2 =

      find_if(v1.begin(),v1.end(),bind1st(equal_to<int>(),3));

   // it2 = v1.begin() + 4

   // Beide Varianten adjacent_find versuchen

   iterator it3 = adjacent_find(v1.begin(),v1.end());

   // it3 = v1.begin() +2

   iterator it4 =

      adjacent_find(v1.begin(),v1.end(),equal_to<int>());

   // v4 = v1.begin() + 2

   // Ausgabe der Ergebnisse

   cout << *it1 << " " << *it2 << " " << *it3 << " "

        << *it4 << endl;

   return 0;

 }


for_each

Algorithmus

Wendet eine Funktion auf jedes Element in einem Bereich an.

Syntax

#include <algorithm>

template <class InputIterator, class Function>

 void for_each(InputIterator first, InputIterator last,

               Function f);

Beschreibung

Der Algorithmus for_each wendet die Funktion f auf alle Elemente der Sequenz im Bereich [first, last) an, wobei first und last Iteratoren sind, die die Sequenz definieren. Da es sich um einen nicht verändernden Algorithmus handelt, kann die Funktion f keine Änderungen der Sequenz vornehmen; sie kann jedoch Ergebnisse durch Nebeneffekte erzielen (beispielsweise durch Kopieren oder Drucken). Wenn f ein Ergebnis zurückgibt, wird dieses ignoriert.

Die Funktion f wird genau last - first mal angewendet.

Beispiel

 #include <vector>

 #include <algorithm>

 using namespace std;

 // Funktionsklasse, die ihr Argument x mal ausgibt

 template <class Arg>

 class out_times_x :  private unary_function<Arg,void>

 {

   private:

      Arg multiplier;

   public:

      out_times_x(const Arg& x) : multiplier(x) { }

      void operator()(const Arg& x)

         { cout << x * multiplier << " " << endl; }

 };

 int main()

 {

   int sequence[5] = {1,2,3,4,5};

   // Erzeugen eines Vektors

   vector<int> v(sequence,sequence + 5);

   // Erzeugen eines Funktionsobjekts

   out_times_x<int> f2(2);

   for_each(v.begin(),v.end(),f2);   // Funktion anwenden

   return 0;

 }


Vorwärtsiterator

Siehe auch	Iterator

Ein Iterator, der sich vorwärts bewegt und Lese- und Schreibzugriff bietet.

Beschreibung

Hinweis:	Eine vollständige Beschreibung der Iteratoren finden Sie im Abschnitt Iteratoren in diesem Referenzteil.

Iteratoren sind eine Generalisierung von Zeigern, durch die ein C++ Programm einheitlich mit verschiedenen Datenstrukturen interagieren kann. Vorwärtsiteratoren bewegen sich vorwärts und besitzen die Fähigkeit, Daten zu lesen und zu schreiben. Diese Iteratoren genügen den unten aufgeführten Anforderungen.

Der folgende Schlüssel bezieht sich auf die unten aufgeführten Iteratoranforderungen:

a und b	Werte vom Typ X

n	Wert vom Typ distance

u, Distance, tmp und m	Bezeichner

r	Wert vom Typ X&

t	Wert vom Typ T

Anforderungen an Vorwärtsiteratoren

Die folgenden Ausdrücke müssen für Vorwärtsiteratoren gültig sein:

X u	u kann einen singulären Wert besitzen

X()	X() kann singulär sein

X(a)	Kopierkonstruktor, a == X(a).

X u(a)	Kopierkonstruktor, u == a

X u = a	Zuweisung, u == a

a == b, a != b	Rückgabewert in bool umwandelbar

*a	Rückgabewert in T& umwandelbar

++r	gibt X& zurück

r++	Rückgabewert in const X& umwandelbar

*r++	gibt T& zurück

Vorwärtsiteratoren unterliegen der Bedingung, daß a == b *a== *b impliziert.

Die Anzahl der Durchgänge, die ein Algorithmus in der Struktur durchführt, unterliegt keiner Einschränkung.

adjacent_find, find, find_first_of


front_insert_iterator, front_inserter

Einfügeiterator

Ein Einfügeiterator, der zum Einfügen von Elementen am Anfang einer Kollektion verwendet wird.

Syntax

#include <iterator>

template <class Container>

 class front_insert_iterator : public output_iterator {

protected:

   Container& container;

public:

   front_insert_iterator (Container& x);

   front_insert_iterator<Container>&

   operator= (const Container::value_type& value);

   front_insert_iterator<Container>& operator* ();

   front_insert_iterator<Container>& operator++ ();

   front_insert_iterator<Container> operator++ (int);

};

 template <class Container>

 front_insert_iterator<Container> front_inserter (Container& x)

Beschreibung

Mit Einfügeiteratoren können Sie neue Elemente in eine Kollektion einfügen, anstatt den Wert eines neuen Elements über den Wert eines vorhandenen Elements zu kopieren. Die Klasse front_insert_iterator wird verwendet, um Elemente am Anfang einer Kollektion einzufügen. Die Funktion front_inserter erzeugt eine Instanz eines front_insert_iterators für einen bestimmten Kollektionstyp. Ein front_insert_iterator kann mit vectoren, deques und listen verwendet werden, nicht jedoch mit maps oder sets.

Beachten Sie, daß ein front_insert_iterator jedes Element, das er einfügt, zum neuen Anfang des Containers macht. Dabei tritt der Effekt ein, daß die Reihenfolge der eingefügten Elemente umgekehrt wird. Wenn Sie beispielsweise einen front_insert_iterator verwenden, um "1", dann "2" und dann "3" am Anfang des Containers exmpl einzufügen, sehen Sie, daß die ersten drei Elemente von exmpl nach drei Einfügungen "3 2 1" sind.

Beispiel

/**************************************************************

 *

 * ins_itr.cpp - Beispielprogramm für einen Einfügeiterator.

 *

 * $Id: ins_itr.cpp,v 1.7 1995/10/06 18:18:03 hart Exp $

 *

 * $$RW_INSERT_HEADER "slyrs.str"

 *

 ***************************************************************/

 #include <iterator>

 #include <deque>

 using namespace std;

 int main ()

 {

  //

  // Initialisieren eines Deques mit einem Array.

  //

  int arr[4] = { 3,4,7,8 };

  deque<int> d(arr+0, arr+4);

  //

  // Ausgabe des Original-Deques.

  //

  cout << "Start with a deque: " << endl << "   ";

  copy(d.begin(), d.end(), ostream_iterator<int>(cout," "));

  //

  // In der Mitte einfügen.

  //

  insert_iterator<deque<int> > ins(d, d.begin()+2);

  *ins = 5; *ins = 6;

  //

  // Ausgabe des neuen Deques.

  //

  cout << endl << endl;

  cout << "Use an insert_iterator: " << endl << "   ";

  copy(d.begin(), d.end(), ostream_iterator<int>(cout," "));

  //

  // Ein Deque aus vier Einsen.

  //

  deque<int> d2(4, 1);

  //

  // d2 vor d einfügen.

  //

  copy(d2.begin(), d2.end(), front_inserter(d));

  //

  // Ausgabe des neuen Deques.

  //

  cout << endl << endl;

  cout << "Use a front_inserter: " << endl << "   ";

  copy(d.begin(), d.end(), ostream_iterator<int>(cout," "));

  //

  // d2 hinter d einfügen.

  //

  copy(d2.begin(), d2.end(), back_inserter(d));

  //

  // Ausgabe des neuen Deques.

  //

  cout << endl << endl;

  cout << "Use a back_inserter: " << endl << "   ";

  copy(d.begin(), d.end(), ostream_iterator<int>(cout," "));

  cout << endl;

  return 0;

 }

Konstruktor

front_insert_iterator (Container& x);

Konstruktor. Erzeugt eine Instanz eines mit Container x verknüpften front_insert_iterators.

Operatoren

front_insert_iterator<Container>&

operator = (const Container::value_type& value);

Fügt eine Kopie von value am Anfang des Containers ein und gibt *this zurück.

front_insert_iterator<Container>&

operator* ();

Gibt *this zurück (den Einfügeiterator selbst).

front_insert_iterator<Container>&

operator++ ();

front_insert_iterator<Container>

operator++ (int);

Inkrementiert den Einfügeiterator und gibt *this zurück.

Hilfsfunktion

template <class Container>

back_insert_iterator<Container>

front_inserter (Container& x)

Gibt einen front_insert_iterator zurück, der Elemente am Anfang von Container x einfügt. Mit dieser Funktion können Sie Einfügeiteratoren, die am Anfang einfügen, inline erzeugen.


Funktionsobjekt

Objekte mit einem definierten operator(). Funktionsobjekte werden anstelle von Zeigern auf Funktionen als Argumente für Template-Algorithmen verwendet.

Syntax

 #include<functional>

 // Typdefinitionen

    template <class Arg, class Result>

    struct unary_function{

         typedef Arg argument_type;

         typedef Result result_type;

    };

    template <class Arg1, class Arg2, class Result>

    struct binary_function{

         typedef Arg1 first_argument_type;

         typedef Arg2 second_argument_type;

         typedef Result result_type;

    };

 // Arithmetische Operationen

   template<class T>

   struct plus : binary_function<T, T, T> {

        T operator() (const T& x, const T& y) const

              { return x + y; }

   };

   template <class T>

   struct minus : binary_function<T, T, T> {

        T operator() (const T& x, const T& y) const

              { return x - y; }

   };

   template <class T>

   struct times : binary_function<T, T, T> {

        T operator() (const T& x, const T& y) const

              { return x * y; }

   };

   template <class T>

   struct divides : binary_function<T, T, T> {

        T operator() (const T& x, const T& y) const

              { return x / y; }

   };

   template <class T>

   struct modulus : binary_function<T, T, T> {

        T operator() (const T& x, const T& y) const

              { return x % y; }

   };

   template <class T>

   struct negate : unary_function<T, T, T> {

        T operator() (const T& x) const

              { return -x; }

   };

 // Vergleiche

    template <class T>

    struct equal_to : binary_function<T, T, bool> {

         bool operator() (const T& x, const T& y) const

               { return x == y; }

    };

    template <class T>

    struct not_equal_to : binary_function<T, T, bool> {

         bool operator() (const T& x, const T& y) const

               { return x != y; }

    };

    template <class T>

    PD 0 struct greater : binary_function<T, T, bool> {

         bool operator() (const T& x, const T& y) const

               { return x > y; }

    };

    template <class T>

    struct less : binary_function<T, T, bool> {

         bool operator() (const T& x, const T& y) const

               { return x < y; }

    };

    template <class T>

    struct greater_equal : binary_function<T, T, bool> {

         bool operator() (const T& x, const T& y) const

               { return x >= y; }

    };

    template <class T>

    struct less_equal : binary_function<T, T, bool> {

         bool operator() (const T& x, const T& y) const

               { return x <= y; }

    };

 // Logische Vergleiche

    template <class T>

    struct logical_and : binary_function<T, T, bool> {

         bool operator() (const T& x, const T& y) const

               { return x && y; }

    };

    template <class T>

    struct logical_or : binary_function<T, T, bool> {

         bool operator() (const T& x, const T& y) const

               { return x || y; }

    };

    template <class T>

    struct logical_not : unary_function<T, T, bool> {

         bool operator() (const T& x, const T& y) const

               { return !x; }

    };

Beschreibung

Funktionsobjekte sind Objekte mit einem definierten operator(). Sie sind wichtig für die effiziente Verwendung der generischen Algorithmen der Standardbibliothek, da die Schnittstelle für jedes algorithmische Template entweder ein Objekt mit einem definierten operator() oder einen Zeiger auf eine Funktion akzeptieren kann. Die Standardbibliothek stellt sowohl eine Standardmenge von Funktionsobjekten als auch zwei Klassen zur Verfügung, die Sie als Grundlage für die Erzeugung Ihrer eigenen Funktionsobjekte verwenden können.

Funktionsobjekte, die ein Argument annehmen, werden unäre Funktionsobjekte genannt. Unäre Funktionsobjekte stellen die Typdefinitionen argument_type und result_type zur Verfügung. Analog werden Funktionsobjekte, die zwei Argumente annehmen, binäre Funktionsobjekte genannt. Binäre Funktionsobjekte stellen die Typdefinitionen first_argument_type, second_argument_type und result_type zur Verfügung.

Die Klassen unary_function und binary_function vereinfachen die Erzeugung binärer Funktionsobjekt-Templates. Die benötigten Typdefinitionen für ein unäres oder binäres Funktionsobjekt werden durch Vererbung von der entsprechenden Funktionsobjektklasse zur Verfügung gestellt.

Die Funktionsobjekte der Standardbibliothek sind unten zusammen mit einer kurzen Beschreibung ihrer Operationen aufgeführt. Diese Klassenreferenz enthält außerdem einen alphabetischen Eintrag für jede Funktion.

Name	Operation

Arithmetische Funktionen

plus	Addition von x + y

minus	Subtraktion von x - y

times	Multiplikation von x * y

divides	Division von x / y

modulus	Rest von x % y

negate	Negation von - x

Vergleichsfunktionen

equal_to	Test auf Gleichheit x == y

not_equal_to	Test auf Ungleichheit x != y

greater	Größer-Vergleich x > y

less	Kleiner-Vergleich x < y

greater_equal	Größer-oder gleich-Vergleich x >= y

less_equal	Kleiner-oder gleich-Vergleich x <= y

Logische Funktionen

logical_and	logische Konjunktion x && y

logical_or	logische Disjunktion x || y

logical_not	logische Negation ! x

Beispiel

 #include<functional>

 #include<deque>

 #include<vector>

 #include<algorithm>

 using namespace std;

 //Erzeugen eines neuen Funktionsobjekts mit unary_function

 template<class Arg>

 class factorial : public unary_function<Arg, Arg>

 {

   public:

   Arg operator()(const Arg& arg)

   {

     Arg a = 1;

     for(Arg i = 2; i <= arg; i++)

       a *= i;

     return a;

   }

 };

 int main()

 {

   //Initialisieren eines Deques mit einem Array aus Integerwerten

   int init[7] = {1,2,3,4,5,6,7};

   deque<int> d(init, init+7);

   //Erzeugen eines leeren Vektors zum Speichern der Faktoren

   vector<int> v((size_t)7);

   //Umwandeln der Zahlen im Deque in ihre Faktoren und

   // Speichern im Vektor

   transform(d.begin(), d.end(), v.begin(), factorial<int>());

   //Ausgabe der Ergebnisse

   cout << "The following numbers: " << endl << "     ";

   copy(d.begin(),d.end(),ostream_iterator<int>(cout," "));

   cout << endl << endl;

   cout << "Have the factorials: " << endl << "     ";

   copy(v.begin(),v.end(),ostream_iterator<int>(cout," "));

   return 0;

 }


generate, generate_n

Algorithmus

Initialisieren eines Containers mit den Werten, die von einer wertgenerierenden Klasse erzeugt wurden.

Syntax

#include <algorithm>

template <class ForwardIterator, class Generator>

  void

  generate(ForwardIterator first, ForwardIterator last,

           Generator gen);

template <class OutputIterator, class Size, class Generator>

  void

  generate_n(OutputIterator first, Size n, Generator gen);

Beschreibung

Eine werterzeugende Funktion liefert bei jedem Aufruf einen Wert zurück. Die Algorithmen generate und generate_n initialisieren eine Sequenz (oder initialisieren sie erneut), indem sie den Rückgabewert der Generatorfunktion gen allen Elementen zuweisen, die durch Iteratoren im Bereich [first, last) oder [first, first + n) bestimmt werden. Die Funktion gen nimmt keine Argumente an. (gen kann eine Funktion oder eine Klasse mit einem definierten operator ()sein, die keine Argumente annimmt.)

generate_n geht davon aus, daß mindestens n Elemente auf first folgen, es sei denn, first ist ein Einfügeiterator.

Die Algorithmen generate und generate_n rufen gen auf und weisen den Rückgabewert genau last - first (oder n) mal zu.

Beispiel

 #include <algorithm>

 #include <vector>

 using namespace std;

 // Wertgenerator verdoppelt einfach den aktuellen Wert

 // und gibt ihn zurück

 template <class T>

 class generate_val

 {

   private:

      T val_;

   public:

      generate_val(const T& val) : val_(val) {}

      T& operator()() { val_ += val_; return val_; }

 };

 int main()

 {

   int d1[4] = {1,2,3,4};

   generate_val<int> gen(1);

   // Erzeugen zweier Vektoren

   vector<int> v1(d1,d1 + 4), v2(d1,d1 + 4);

   // Erzeugen eines leeren Vektors

   vector<int> v3;

   // Erzeugen von Werten für alle Elemente von v1

   generate(v1.begin(),v1.end(),gen);

   // Erzeugen von Werten für die ersten 3 Elemente von v2

   generate_n(v2.begin(),3,gen);

   // Einfügeiterator zum Generieren von 5 Werten für v3 verwenden

   generate_n(back_inserter(v3),5,gen);

   // Alle drei nach cout kopieren

   ostream_iterator<int> out(cout," ");

   copy(v1.begin(),v1.end(),out);

   cout << endl;

   copy(v2.begin(),v2.end(),out);

   cout << endl;

   copy(v3.begin(),v3.end(),out);

   cout << endl;

   // 3 Werte für cout generieren

   generate_n(ostream_iterator<int>(cout," "),3,gen);

   cout << endl;

   return 0;

 }


get_temporary_buffer

Siehe auch	Primitiv zur Speicherverwaltung

Zeigerbasiertes Primitiv zur Speicherverwaltung.

Syntax

#include <memory>

template <class T>

 pair<T*, ptrdiff_t> get_temporary_buffer (ptrdiff_t n, T*);

Beschreibung

Die Template-Funktion get_temporary_buffer reserviert im Systemspeicher den größten möglichen Puffer, der kleiner oder gleich der angeforderten Größe ist (n*sizeof(T)) und gibt ein pair<T* ptrdiff_t> zurück, das die Adresse und die Größe dieses Puffers enthält. Die Einheiten, die zur Beschreibung der Kapazität verwendet werden, sind sizeof(T).


greater

Funktionsobjekt

Binäres Funktionsobjekt, das true zurückgibt, wenn sein erstes Argument größer als das zweite ist.

Syntax

#include <functional>

    template <class T>

         struct greater : binary_function<T, T, bool> {

         bool operator() (const T& x, const T& y) const

               { return x > y; }

    };

Beschreibung

greater ist ein binäres Funktionsobjekt. Sein operator() gibt true zurück, wenn x größer als y ist. Sie können ein Objekt greater an jeden Algorithmus übergeben, der eine binäre Funktion benötigt. Beispielsweise wendet der Algorithmus transform eine binäre Operation auf die entsprechenden Werte in zwei Kollektionen an und speichert das Ergebnis der Funktion. greater wird in diesem Algorithmus auf die folgende Weise verwendet:

vector<int> vec1;

vector<int> vec2;

vector<int> vecResult;

.

.

.

transform(vec1.begin(), vec1.end(),
          vec2.begin(), vec2.end(),
          vecResult.begin(), greater<int>());

Nach diesem Aufruf von transform enthält vecResult(n) eine "1", wenn vec1(n) größer als vec2(n) war oder eine "0", wenn vec1(n) kleiner oder gleich vec2(n) war.


greater_equal

Funktionsobjekt

Binäres Funktionsobjekt, das true zurückgibt, wenn sein erstes Argument größer als oder gleich seinem zweiten Argument ist.

Syntax

#include <functional>

    template <class T>

    struct greater_equal : binary_function<T, T, bool> {

         bool operator() (const T& x, const T& y) const

               { return x >= y; }

    };

Beschreibung

greater_equal ist ein binäres Funktionsobjekt. Sein operator() gibt true zurück, wenn x größer oder gleich y ist. Sie können ein Objekt greater_equal an jeden Algorithmus übergeben, der eine binäre Funktion benötigt. Beispielsweise kann der Algorithmus sort eine binäre Funktion als alternatives Vergleichsobjekt zum Sortieren einer Sequenz akzeptieren. greater_equal wird in diesem Algorithmus auf die folgende Weise verwendet:

vector<int> vec1;

.

.

sort(vec1.begin(), vec1.end(),greater_equal<int>());

Nach diesem Aufruf von sort wird vec1 in absteigender Folge sortiert.


Heap-Operationen

Siehe auch	Algorithmus

Siehe Einträge für make_heap, pop_heap, push_heap und sort_heap.


includes

Siehe auch	Algorithmus

Grundlegende Mengenoperation für sortierte Sequenzen.

Syntax

#include <algorithm>

template <class InputIterator1, class InputIterator2>

 bool

includes (InputIterator1 first1, InputIterator1 last1,

          InputIterator2 first2, InputIterator2 last2);

template <class InputIterator1, class InputIterator2, class Compare>

 bool

includes (InputIterator1 first1, InputIterator1 last1,

          InputIterator2 first2, InputIterator2 last2, Compare comp);

Beschreibung

Der Algorithmus includes vergleicht zwei sortierte Sequenzen und gibt true zurück, wenn jedes Element des Bereichs [first2, last2) im Bereich [first1, last1) enthalten ist. Andernfalls wird false zurückgegeben. include geht davon aus, daß die Sequenzen mit dem Standardvergleichsoperator kleiner als (<) sortiert wurden, wenn kein alternativer Vergleichsoperator (comp) zur Verfügung gestellt wird.

Höchstens((last1 - first1) + (last2 - first2)) * 2 -1 Vergleiche werden durchgeführt.

Beispiel

 #include<algorithm>

 #include<set>

 using namespace std;

 int main()

 {

   //Initialisieren einiger Mengen

   int a1[10] = {1,2,3,4,5,6,7,8,9,10};

   int a2[6]  = {2,4,6,8,10,12};

   int a3[4]  = {3,5,7,8};

   set<int, less<int> > all(a1, a1+10), even(a2, a2+6),

                         small(a3,a3+4);

  //includes demonstrieren

  cout << "The set: ";

  copy(all.begin(),all.end(),

        ostream_iterator<int>(cout," "));

  bool answer = includes(all.begin(), all.end(),

                small.begin(), small.end());

  cout << endl

       << (answer ? "INCLUDES " : "DOES NOT INCLUDE ");

  copy(small.begin(),small.end(),

        ostream_iterator<int>(cout," "));

  answer = includes(all.begin(), all.end(),

                    even.begin(), even.end());

  cout << ", and" << endl

       << (answer ? "INCLUDES" : "DOES NOT INCLUDE ");

  copy(even.begin(),even.end(),

        ostream_iterator<int>(cout," "));

  cout << endl << endl;

  return 0;

 }


inner_product

Generalisierte numerische Operation

Berechnet das innere Produkt A X B zweier Bereiche A und B.

Syntax

#include <numeric>

template <class InputIterator1,

          class InputIterator2,

          class T              >

T inner_product (InputIterator1 first1,

                InputIterator1 last1,

                InputIterator2 first2, T init);

template <class InputIterator1,

          class InputIterator2,

          class T,

          class BinaryOperation1,

          class BinaryOperation last1>

T inner_product (InputIterator1 first1,

                InputIterator1 last1,

                InputIterator2 first2,

                T init,

                BinaryOperation1 binary_op1,

                BinaryOperation binary_op2);

Beschreibung

Es gibt zwei Versionen von inner_product. Die erste Version berechnet das innere Produkt, indem sie die Standardmultiplikations- und Additionsoperatoren verwendet, während die zweite Version die Angabe binärer Operationen erlaubt, die anstelle der Standardoperationen verwendet werden sollen.

Die erste Version der Funktion berechnet ihr Ergebnis, indem sie den Akkumulator acc mit dem Anfangswert init initialisiert und anschließend die Modifikation:

acc = acc + (*i1) * (*i2)

für jeden Iterator i1 im Bereich [first1, last1) und jeden Iterator i2 im Bereich [first2, first2 + (last1 - first1)) in der Reihenfolge durchführt. Der Algorithmus gibt acc zurück.

Die zweite Version der Funktion initialisiert acc mit init und berechnet anschließend das Ergebnis:

acc  =  binary_op1(acc, binary_op2(*i1,  *i2))

für jeden Iterator i1 im Bereich [first1, last1) und jeden Iterator i2 im Bereich [first2, first2 + (last1 - first1)) in der Reihenfolge.

Der Algorithmus inner_product berechnet genau (last1 - first1) Anwendungen entweder von

acc + (*i1) * (*i2)

oder von

binary_op1(acc, binary_op2(*i1, *i2)).

Beispiel

 #include <numeric>       //Für inner_product

 #include <list>          //Für Listen

 #include <vector>        //Für Vectoren

 #include <functional>    //Für Plus und Minus

 using namespace std;

 int main()

 {

   //Initialisieren einer Liste und eines Integerwerts mit Arrays aus Integerwerten

   int a1[3] = {6, -3, -2};

   int a2[3] = {-2, -3, -2};

   list<int>   l(a1, a1+3);

   vector<int> v(a2, a2+3);

   //Berechnen des inneren Produkts der zwei Wertemengen

   int inner_prod =

        inner_product(l.begin(), l.end(), v.begin(), 0);

   //Berechnen eines "verrückten" (wacky) inneren Produkts unter Verwendung der gleichen Werte

   int wacky =

         inner_product(l.begin(), l.end(), v.begin(), 0,

                             plus<int>(), minus<int>());

   //Drucken der Ausgabe

   cout << "For the two sets of numbers: " << endl

        << "     ";

   copy(v.begin(),v.end(),ostream_iterator<int>(cout," "));

   cout << endl << " and  ";

   copy(l.begin(),l.end(),ostream_iterator<int>(cout," "));

   cout << "," << endl << endl;

   cout << "The inner product is: " << inner_prod << endl;

   cout << "The wacky result is: " << wacky << endl;

   return 0;

 }


inplace_merge

Siehe auch	Algorithmus

Führt zwei sortierte Sequenzen in einer zusammen.

Syntax

#include <algorithm>

template <class BidirectionalIterator>

  void

  inplace_merge(BidirectionalIterator first,

                BidirectionalIterator middle,

                BidirectionalIterator last);

template <class BidirectionalIterator, class Compare>

  void

  inplace_merge(BidirectionalIterator first,

                BidirectionalIterator middle,

                BidirectionalIterator last, Compare comp);

Beschreibung

Der Algorithmus inplace_merge führt zwei sortierte, aufeinanderfolgende Bereiche [first, middle) und [middle, last) zusammen und fügt das Ergebnis der Zusammenführung in den Bereich [first, last) ein. Der Zusammenführungsvorgang ist stabil, d.h., wenn die beiden Bereiche äquivalente Elemente enthalten, werden die Elemente aus dem ersten Bereich immer den Elementen aus dem zweiten Bereich vorangestellt.

Der Algorithmus inplace_merge besitzt zwei Versionen. Die erste Version verwendet den Operator kleiner als (<) als Standardvergleichsoperator; die zweite Version nimmt ein drittes Argument an, das einen Vergleichsoperator angibt.

Wenn genügend zusätzlicher Speicher verfügbar ist, führt , inplace_merge höchstens (last - first) -1 Vergleiche durch. Wenn kein zusätzlicher Speicher zur Verfügung steht, kann ein Algorithmus mit der Komplexität O(NlogN) verwendet werden.

Beispiel

#include <algorithm>

 #include <vector>

 using namespace std;

 int main()

 {

   int d1[4] = {1,2,3,4};

   int d2[8] = {11,13,15,17,12,14,16,18};

   // Erzeugen zweier Vektoren

   vector<int> v1(d1,d1 + 4), v2(d1,d1 + 4);

// Erzeugen von vier Zielvektoren

   vector<int> v3(d2,d2 + 8),v4(d2,d2 + 8),

               v5(d2,d2 + 8),v6(d2,d2 + 8);

   // Erzeugen eines leeren Vektors

   vector<int> v7;

   // Zusammenführen von v1 mit v2

   merge(v1.begin(),v1.end(),v2.begin(),v2.end(),v3.begin());

   // Vergleichsoperator verwenden

   merge(v1.begin(),v1.end(),v2.begin(),v2.end(),v4.begin(),
         less<int>());

   // Stationäres Zusammenführen von v5

   vector<int>::iterator mid = v5.begin();

   advance(mid,4);

   inplace_merge(v5.begin(),mid,v5.end());

   // Vergleichsoperator für v6 verwenden

   mid = v6.begin();

   advance(mid,4);

   inplace_merge(v6.begin(),mid,v6.end(),less<int>());

   // v1 und v2 im leeren Vektor mit Einfügeiterator zusammenführen

   merge(v1.begin(),v1.end(),v2.begin(),v2.end(),

         back_inserter(v7));

   // Alles nach cout kopieren

   ostream_iterator<int> out(cout," ");

   copy(v1.begin(),v1.end(),out);

   cout << endl;

   copy(v2.begin(),v2.end(),out);

   cout << endl;

   copy(v3.begin(),v3.end(),out);

   cout << endl;

   copy(v4.begin(),v4.end(),out);

   cout << endl;

   copy(v5.begin(),v5.end(),out);

   cout << endl;

   copy(v6.begin(),v6.end(),out);

   cout << endl;

   copy(v7.begin(),v7.end(),out);

   cout << endl;

   // v1 und v2 in cout zusammenführen

   merge(v1.begin(),v1.end(),v2.begin(),v2.end(),

         ostream_iterator<int>(cout," "));

   cout << endl;

   return 0;

 }


Eingabeiterator

Siehe auch	Iterator

Ein Vorwärtsiterator mit nur Lesezugriff.

Beschreibung

Hinweis:	Eine vollständige Beschreibung der Iteratoren finden Sie im Abschnitt Iteratoren in diesem Referenzteil.

Iteratoren sind eine Generalisierung von Zeigern, durch die ein C++ Programm einheitlich mit verschiedenen Datenstrukturen interagieren kann. Eingabeiteratoren sind Vorwärtsiteratoren, die nur Lesezugriff besitzen und den unten aufgeführten Bedingungen genügen.

Der folgende Schlüssel bezieht sich auf die unten aufgeführten Iteratorbeschreibungen:

a und b	Werte vom Typ X

n	Wert vom Typ distance

u, Distance, tmp und m	Bezeichner

r	Wert vom Typ X&

t	Wert vom Typ T

Anforderungen an Eingabeiteratoren

Die folgenden Ausdrücke müssen für Eingabeiteratoren gültig sein:

X(a)	Kopierkonstruktor, a == X(a).

X u(a)	Kopierkonstruktor, u == a

X u = a	Zuweisung, u == a

a == b, a != b	Rückgabewert in bool umwandelbar

*a	Rückgabewert in T& umwandelbar

++r	gibt X& zurück

r++	Rückgabewert in const X& umwandelbar

*r++	gibt Typ T zurück

Bei Eingabeiteratoren impliziert  a == b nicht ++a == ++b.

Algorithmen, die Eingabeiteratoren verwenden, sollten Single-Pass-Algorithmen sein. D.h. sie sollten den gleichen Iterator nicht zweimal durchlaufen.

Der Wert vom Typ T muß kein lvalue sein.


insert_iterator, Inserter

Einfügeiterator

Ein Einfügeiterator, der verwendet wird, um Elemente in eine Kollektion einzufügen anstatt sie zu überschreiben.

Syntax

#include <iterator>

template <class Container>

 class insert_iterator : public output_iterator {

protected:

    Container& container;

    Container::iterator iter;

public:

   insert_iterator (Container& x, Container::iterator i);

   insert_iterator<Container>&

   operator= (const Container::value_type& value);

   insert_iterator<Container>& operator* ();

   insert_iterator<Container>& operator++ ();

   insert_iterator<Container> operator++ (int);

};

template <class Container>

insert_iterator<Container> inserter (Container& x, Iterator i)

Beschreibung

Mit Einfügeiteratoren können Sie neue Elemente in eine Kollektion einfügen, anstatt den Wert eines neuen Elements über den Wert eines vorhandenen Elements zu kopieren. Die Klasse insert_iterator wird verwendet, um Elemente an einer bestimmten Stelle in eine Kollektion einzufügen. Die Funktion inserter erzeugt eine Instanz eines insert_iterators, wenn ein bestimmter Kollektionstyp und ein Iterator gegeben ist . Ein insert_iterator kann mit vectoren, deques, lists, maps und sets verwendet werden.

Beispiel

 #include<iterator>

 #include<vector>

 using namespace std;

 int main()

 {

   //Initialisieren eines Vektors mit einem Array

   int arr[4] = {3,4,7,8};

   vector<int> v(arr,arr+4);

   //Ausgabe des Original-Vektors

   cout << "Start with a vector: " << endl << "     ";

   copy(v.begin(),v.end(),ostream_iterator<int>(cout," "));

   //In der Mitte einfügen

   insert_iterator<vector<int> >  ins(v, v.begin()+2);

   *ins = 5;

   *ins = 6;

   //Ausgabe des neuen Vektors

   cout << endl << endl;

   cout << "Use an insert_iterator: " << endl << "     ";

   copy(v.begin(),v.end(),ostream_iterator<int>(cout," "));

   return 0;

 }

Konstruktor

insert_iterator (Container& x, Container::iterator i);

Konstruktor. Erzeugt eine Instanz eines mit Container x und Iterator i verknüpften insert_iterators.

Operatoren

insert_iterator<Container>&

operator= (const Container::value_type& value);

Fügt eine Kopie von value an der durch insert_iterator angegebenen Stelle in den Container ein, inkrementiert den Iterator und gibt *this zurück.

insert_iterator<Container>&

operator* ();

Gibt *this zurück (den Eingabeiterator selbst).

insert_iterator<Container>&

operator++ ();

insert_iterator<Container>

operator++ (int);

Inkrementiert den Einfügeiterator und gibt *this zurück.

Hilfsfunktion

template <class Container, class Iterator>

insert_iterator<Container>

inserter (Container& x, Iterator i)

Gibt einen insert_iterator zurück, der Elemente an Position i in den Container x einfügt. Mit dieser Funktion können Sie Einfügeiteratoren inline erzeugen.


Einfügeiterator

Einfügeiterator

Iteratorenadapter, der es einem Iterator erlaubt, Elemente in einen Container einzufügen anstatt die Elemente im Container zu überschreiben.

Syntax

#include <iterator>

template <class Container>

 class back_insert_iterator : public output_iterator {

protected:

   Container& container;

public:

   back_insert_iterator (Container& x);

   back_insert_iterator<Container>&

   operator= (const Container::value_type& value);

   back_insert_iterator<Container>& operator* ();

   back_insert_iterator<Container>& operator++ ();

   back_insert_iterator<Container> operator++ (int);

};

template <class Container>

 class front_insert_iterator : public output_iterator {

protected:

   Container& container;

public:

   front_insert_iterator (Container& x);

   front_insert_iterator<Container>&

   operator= (const Container::value_type& value);

   front_insert_iterator<Container>& operator* ();

   front_insert_iterator<Container>& operator++ ();

   front_insert_iterator<Container> operator++ (int);

};

template <class Container>

 class insert_iterator : public output_iterator {

protected:

    Container& container;

    Container::iterator iter;

public:

   insert_iterator (Container& x, Container::iterator i);

   insert_iterator<Container>&

   operator= (const Container::value_type& value);

   insert_iterator<Container>& operator* ();

insert_iterator<Container>& operator++ ();

   insert_iterator<Container> operator++ (int);

};

 template <class Container>

 back_insert_iterator<Container> back_inserter (Container& x)

 template <class Container>

 front_insert_iterator<Container> front_inserter (Container& x)

 template <class Container>

 insert_iterator<Container> inserter (Container& x, Iterator i)

Beschreibung

Einfügeiteratoren sind Iteratorenadapter, durch die ein Iterator neue Elemente in eine Kollektion einfügen kann, anstatt die vorhandenen Elemente beim Kopieren in einen Container zu überschreiben. Es gibt mehrere Typen von Einfügeiteratorenklassen.

Die Klasse back_insert_iterator wird verwendet, um Elemente am Ende einer Kollektion einzufügen. Die Funktion back_inserter kann mit einem Iterator inline verwendet werden, um eine Instanz eines back_insert_iterators für einen bestimmten Kollektionstyp zu erzeugen.

Die Klasse front_insert_iterator wird verwendet, um Elemente am Anfang einer Kollektion einzufügen. Die Funktion front_inserter erzeugt eine Instanz eines front_insert_iterators für einen bestimmten Kollektionstyp.

Ein insert_iterator fügt neue Elemente in eine Kollektion an einer Position ein, die durch einen dem Konstruktor zur Verfügung gestellten Iterator definiert wird. Ebenso wie die anderen Einfügeiteratoren besitzt insert_iterator eine Hilfsfunktion namens inserter, die eine Kollektion und einen Iterator für die Kollektion annimmt und eine Instanz des insert_iterators erzeugt.


istream_iterator

Siehe auch	Iteratoren

Stream-Iterator, der istreams Iteratorenfähigkeiten zur Verfügung stellt. Mit diesem Iterator können generische Algorithmen direkt mit Streams verwendet werden.

Syntax

#include <iterator>

Istream iterator

template class T, class Distance = ptrdiff_t>

 class istream_iterator : public input_iterator<T, Distance>

 {

 public:

  istream_iterator();

  istream_iterator (istream& s);

  istream_iterator (const istream_iterator <T, Distance>& x);

  ~istream_itertor ();

  const T& operator*() const;

  istream_iterator <T, Distance>& operator++();

  istream_iterator <T, Distance> operator++ (int)

 };

Beschreibung

Stream-Iteratoren stellen die Standarditeratorenschnittstelle für Eingabe- und Ausgabe-Streams zur Verfügung.

Die Klasse istream_iterator liest Elemente aus einem Eingabe-Stream. Ein Wert von T wird ermittelt und gespeichert, wenn der Iterator konstruiert wird sowie bei jedem Aufruf von operator++. Der Iterator ist gleich dem Wert des Stream-Ende-Iterators, wenn das Dateiende erreicht wird. Verwenden Sie den Konstruktor ohne Argumente, um einen Dateiende-Iterator zu erzeugen. Die einzig zulässige Verwendung dieses Iterators ist der Vergleich mit anderen Iteratoren, um das Dateiende zu prüfen. Versuchen Sie nicht, den Stream-Ende-Iterator zu dereferenzieren; er besitzt die gleiche Funktionalität wie der Nach-Bereichsende-Iterator, die von der Funktion end() von Containern zur Verfügung gestellt wird. Da ein istream_iterator ein Eingabeiterator ist, können Sie dem durch das Dereferenzieren des Iterators zurückgegebenen Wert nichts zuweisen. Dies bedeutet auch, daß istream_iteratoren nur für Algorithmen mit einem Durchgang verwendet werden können.

Da bei jeder Anwendung des operator++ auf istream_iterator ein neuer Wert gelesen wird, bewahrt diese Operation nicht die Gleichheit. Das bedeutet, daß i == j nicht bedeutet, daß ++i == ++j ist (obwohl zwei Stream-Ende-Iteratoren immer gleich sind).

Konstruktoren

istream_iterator ();

Konstruiert einen Stream-Ende-Iterator. Dieser Iterator kann für den Vergleich mit einer Dateiende-Bedingung verwendet werden. Verwenden Sie ihn, um Algorithmen mit Ende-Iteratoren auszustatten.

istream_iterator (istream& s);

Konstruiert einen istream_iterator für den gegebenen Stream.

istream_iterator (const istream_iterator<T, Distance>& x);

Kopierkonstruktor.

Destruktoren

~istream_iterator ();

Destruktor.

Operatoren

const T& operator* () const;

Gibt den aktuellen vom Iterator gespeicherten Wert zurück.

istream_iterator<T, Distance>&

operator++ ()

istream_iterator<T, Distance>

operator++ (int)

Ermittelt das nächste Element aus dem Eingabe-Stream.

Beispiel

 #include <iterator>

 #include <vector>

 #include <numeric>

 using namespace std;

 int main ()

 {

  vector<int> d;

  int total = 0;

  //

  // Werte von cin bis Dateiende sammeln

  // Beachten Sie die Verwendung des Standardkonstruktors für den Ende-Iterator

  //

  cout << "Enter a sequence of integers (eof to quit): " ;

  copy(istream_iterator<int,vector<int>::difference_type>(cin),

    istream_iterator<int,vector<int>::difference_type>(),

    inserter(d,d.begin()));

  //

  // ganzen Vektor und die Summe nach cout streamen

  //

  copy(d.begin(),d.end()-1,ostream_iterator<int>(cout," + "));

  if (d.size())

   cout << *(d.end()-1) << " = " <<

     accumulate(d.begin(),d.end(),total) << endl;

  return 0;

 }


Iteratoren

Zeigergeneralisierungen zur Bewegung durch Kollektionen und zum Modifizieren von Kollektionen.

Beschreibung

Iteratoren sind eine Generalisierung von Zeigern, durch die ein C++ Programm einheitlich mit verschiedenen Datenstrukturen interagieren kann. Die Abbildung unten zeigt die fünf Iteratorkategorien, die durch die Standardbibliothek definiert werden, sowie ihre hierarchische Beziehung. Da die Iteratorkategorien der Standardbibliothek hierarchisch sind, enthält jede Kategorie alle Anforderungen der ihr übergeordneten Kategorien.

Da Iteratoren verwendet werden, um sich durch Container zu bewegen und um auf diese zuzugreifen, bestimmt der Containertyp den Iteratorentyp, den er generiert. Da Algorithmen außerdem bestimmte Iteratorentypen als Argumente verlangen, bestimmen Iteratoren zum größten Teil, welche Algorithmen der Standardbibliothek mit den Containern der Standardbibliothek verwendet werden können.

Um konform zum C++ Standard zu sein, müssen alle Container- und Sequenzklassen ihre eigenen Iteratoren zur Verfügung stellen. Eine Instanz eines Iterators eines Containers oder einer Sequenz kann auf eine der folgenden Arten deklariert werden:

class name ::iterator

class name ::const_iterator

Container und Sequenzen müssen dem Anfang und dem Ende ihrer Kollektionen außerdem const-Iteratoren zur Verfügung stellen. Auf diese kann über die Klassenelemente begin() und end() zugegriffen werden.

Die Semantik der Iteratoren ist eine Generalisierung der Semantik von C++ Zeigern. Jede Template-Funktion, die Iteratoren annimmt, funktioniert mit C++ Zeigern zum Verarbeiten typisierter, aufeinanderfolgender Speichersequenzen.

Iteratoren können konstant oder veränderbar sein - abhängig davon, ob sich das Ergebnis von operator * als Referenz oder als Referenz auf eine Konstante verhält. Konstante Iteratoren können den Anforderungen eines output_iterators nicht genügen.

Jeder Iteratorentyp garantiert, daß es einen Iteratorenwert gibt, der hinter das letzte Element eines entsprechenden Containers zeigt. Dieser Wert wird Nach-Bereichsende-Wert genannt. Es wird jedoch nicht garantiert, daß dieser Wert dereferenzierbar ist.

Jede Funktion, die ein Iterator zur Verfügung stellt, muß in amortisierter konstanter Ausführungszeit ausgeführt werden können.

Der folgende Schlüssel bezieht sich auf alle Iteratorenanforderungsbeschreibungen in diesem Abschnitt:

a und b	Werte vom Typ X.

n	Wert vom Typ distance.

u, Distance, tmp und m	Bezeichner.

r	Wert vom Typ X&.

t	Wert vom Typ T.

Anforderungen an bidirektionale Iteratoren

Die folgenden Ausdrücke müssen für Eingabeiteratoren gültig sein:

X(a)	Kopierkonstruktor, a == X(a).

X u(a)	Kopierkonstruktor, u == a.

X u = a	Zuweisung, u == a.

a == b, a != b	Rückgabewert in bool umwandelbar.

*a	Rückgabewert in T& umwandelbar.

++r	Gibt X& zurück.

r++	Rückgabewert in const X& umwandelbar.

*r++	Gibt Typ T zurück.

Bei Eingabeiteratoren impliziert  a == b nicht ++a == ++b.

Algorithmen, die Eingabeiteratoren verwenden, sollten Single-Pass-Algorithmen sein. Das heißt, sie sollten den gleichen Iterator nicht zweimal durchlaufen.

Der Wert vom Typ T muß kein lvalue sein.

Anforderungen an Ausgabeiteratoren

Die folgenden Ausdrücke müssen für Ausgabeiteratoren gültig sein:

X(a)	Kopierkonstruktor, a == X(a).

X u(a)	Kopierkonstruktor, u == a.

X u = a	Zuweisung, u == a.

*a = t	Ergebnis wird nicht verwendet.

++r	Liefert X& zurück.

r++	Rückgabewert kann in const X& umgewandelt werden.

*r++ = t	Ergebnis wird nicht verwendet.

Der Operator * kann nur auf der linken Seite einer Zuweisung verwendet werden.

Algorithmen, die Ausgabeiteratoren verwenden, sollten Single-Pass-Algorithmen sein. Das heißt, der gleiche Iterator sollte nicht zweimal durchlaufen werden.

Anforderungen an Vorwärtsiteratoren

Die folgenden Ausdrücke müssen für Vorwärtsiteratoren gültig sein:

X u	u kann einen singulären Wert besitzen.

X()	X() kann singulär sein.

X(a)	Kopierkonstruktor, a == X(a).

X u(a)	Kopierkonstruktor, u == a.

X u = a	Zuweisung, u == a.

a == b, a != b	Rückgabewert in bool umwandelbar.

*a	Rückgabewert in T& umwandelbar.

++r	gibt X& zurück.

r++	Rückgabewert in const X& umwandelbar.

*r++	gibt T& zurück.

Vorwärtsiteratoren erfüllen die Bedingung, daß a == b *a== *b impliziert.

Die Anzahl der Durchgänge, die ein Algorithmus in der Struktur durchführt, unterliegt keiner Einschränkung.

Anforderungen an bidirektionale Iteratoren

Ein bidirektionaler Iterator muß alle Anforderungen an Vorwärtsiteratoren erfüllen. Außerdem müssen die folgenden Ausdrücke gültig sein:

--r	Gibt X& zurück.

r--	Rückgabewert in const X& umwandelbar.

*r--	Gibt T& zurück.

Anforderungen an Random Access-Iteratoren

Ein Random Access-Iterator muß alle Anforderungen an Vorwärtsiteratoren erfüllen. Außerdem müssen die folgenden Ausdrücke gültig sein:

r += n	Semantik von --r oder ++r n hängt vom Vorzeichen von n ab.
a + n, n + a	Liefert den Typ X zurück.
r -= n	Liefert X& zurück, verhält sich wie r += -n.
a - n	Liefert den Typ X zurück.
b - a	Liefert Distance zurück.
a[n]	*(a+n), Rückgabewert kann in T umgewandelt werden.
a < b	Über-Alles-Ordnungsrelation.
a > b	umgekehrte Über-Alles-Ordnungsrelation zu <
a <= b	!(a < b)
a >= b	!(a > b)
Alle relationalen Operatoren liefern einen Wert zurück, der in bool umgewandelt werden kann.


iter_swap

Siehe auch	Algorithmus

Vertauscht Werte, auf die an zwei Positionen gezeigt wird.

Syntax

#include <algorithm>

template <class ForwardIterator1, class ForwardIterator2>

 void iter_swap (ForwardIterator1 a, ForwardIterator2 b);

Beschreibung

Der Algorithmus iter_swap vertauscht die Werte, auf die die beiden Iteratoren a und b zeigen.

Beispiel

/****************************************************************

 *

 * swap.cpp - Beispielprogramm für den Vertauschalgorithmus.

 *

 * $Id: swap.cpp,v 1.7 1995/10/06 20:05:43 hart Exp $

 *

 * $$RW_INSERT_HEADER "slyrs.str"

 *

 ****************************************************************/

 #include <vector>

 #include <algorithm>

 using namespace std;

 int main ()

 {

  int d1[] = {6, 7, 8, 9, 10, 1, 2, 3, 4, 5};

  //

  // Erzeugen eines Vektors.

  //

  vector<int> v(d1+0, d1+10);

  //

  // Ausgabe des Originalvektors.

  //

  cout << "For the vector: ";

  copy(v.begin(), v.end(), ostream_iterator<int>(cout," "));

  //

  // Vertauschen der ersten fünf Elemente mit den letzten fünf Elementen.

  //

  swap_ranges(v.begin(), v.begin()+5, v.begin()+5);

  //

  // Ausgabe des Ergebnisses.

  //

  cout << endl << endl

    << "Swaping the first 5 elements with the last 5 gives: "

    << endl << "   ";

  copy(v.begin(), v.end(), ostream_iterator<int>(cout," "));

  //

  // Nun ein Beispiel für iter_swap -- erstes und letztes Element vertauschen.

  //

  iter_swap(v.begin(), v.end()-1);

  //

  // Ausgabe des Ergebnisses.

  //

  cout << endl << endl

    << "Swaping the first and last elements gives: "

    << endl << "   ";

  copy(v.begin(), v.end(), ostream_iterator<int>(cout," "));

  cout << endl;

  return 0;

 }


less

Funktionsobjekt

Binäres Funktionsobjekt, das true zurückgibt, wenn sein erstes Argument kleiner als sein zweites Argument ist.

Syntax

#include<functional>

    template <class T>

         struct less : binary_function<T, T, bool> {

         bool operator() (const T& x, const T& y) const

               { return x < y; }

    };

Beschreibung

less ist ein binäres Funktionsobjekt. Sein operator() gibt true zurück, wenn x kleiner als y ist. Sie können ein Objekt less an jeden Algorithmus übergeben, der eine binäre Funktion benötigt. Beispielsweise wendet der Algorithmus transform eine binäre Operation auf die entsprechenden Werte in zwei Kollektionen an und speichert das Ergebnis der Funktion. less wird in diesem Algorithmus auf die folgende Weise verwendet:

vector<int> vec1;

vector<int> vec2;

vector<int> vecResult;

.

.

.

transform(vec1.begin(), vec1.end(),
          vec2.begin(), vec2.end(),
          vecResult.begin(), less<int>());

Nach diesem Aufruf von transform enthält vecResult(n) eine "1", wenn vec1(n) kleiner als vec2(n) war oder eine "0", wenn vec1(n) größer oder gleich vec2(n) war.


less_equal

Funktionsobjekt

Binäres Funktionsobjekt, das true zurückgibt, wenn sein erstes Argument kleiner als oder gleich seinem zweiten Argument ist.

Syntax

#include<functional>

    template <class T>

    struct less_equal : binary_function<T, T, bool> {

         bool operator() (const T& x, const T& y) const

               { return x <= y; }

    };

Beschreibung

less_equal ist ein binäres Funktionsobjekt. Sein operator() gibt true zurück, wenn x kleiner oder gleich y ist. Sie können ein Objekt less_equal an jeden Algorithmus übergeben, der eine binäre Funktion benötigt. Beispielsweise kann der Algorithmus sort eine binäre Funktion als alternatives Vergleichsobjekt zum Sortieren einer Sequenz akzeptieren. less_equal wird in diesem Algorithmus auf die folgende Weise verwendet:

vector<int> vec1;

.

.

sort(vec1.begin(), vec1.end(),greater_equal<int>());

Nach diesem Aufruf von sort wird vec1 in aufsteigender Folge sortiert.


lexicographical_compare

Algorithmus

Vergleicht zwei Bereiche lexikographisch.

Syntax

#include <algorithm>

template <class InputIterator1, class InputIterator2>

 bool

 lexicographical_compare(InputIterator1 first,

                         InputIterator2 last1,

                         InputIterator2 first2,

                         InputIterator last2);

template <class InputIterator1, class InputIterator2,

          class Compare>

 bool

 lexicographical_compare(InputIterator1 first,

                         InputIterator2 last1,

                         InputIterator2 first2,

                         InputIterator last2, Compare comp);

Beschreibung

Die Funktionen lexicographical_compare vergleichen jedes Element des Bereichs [first1, last1) mit dem entsprechenden Element im Bereich [first2, last2) und verwenden dabei die Iteratoren i und j.

Die erste Version des Algorithmus verwendet < als Standardvergleichsoperator. Sie gibt sofort true zurück, wenn sie ein Paar findet, für das *i kleiner als *j ist und false, wenn *j kleiner als *i ist. Wenn der Algorithmus das Ende der ersten Sequenz erreicht, bevor das Ende der zweiten Sequenz erreicht ist, wird ebenfalls true zurückgegeben.

Die zweite Version der Funktion nimmt ein Argument comp an, das eine Vergleichsfunktion definiert, die anstelle des Standardoperators < verwendet wird.

Die Funktionen lexicographic_compare können mit allen Datentypen der Standardbibliothek verwendet werden.

lexicographical_compare führt höchstens min((last1 - first1), (last2 - first2)) Anwendungen der Vergleichsfunktion durch.

Beispiel

 #include <algorithm>

 #include <vector>

 using namespace std;

 int main(void)

 {

   int d1[5] = {1,3,5,32,64};

   int d2[5] = {1,3,2,43,56};

   // Erzeugen eines Vektors

   vector<int> v1(d1,d1 + 5), v2(d2,d2 + 5);

   // Ist v1 kleiner als v2 (ich glaube nicht)

   bool b1 =  lexicographical_compare(v1.begin(),

              v1.end(), v2.begin(), v2.end());

   // Ist v2 kleiner als v1 (ja, sicher)

   bool b2 =  lexicographical_compare(v2.begin(),

            v2.end(), v1.begin(), v1.end(), less<int>());

   cout << (b1 ? "TRUE" : "FALSE") << " "

        << (b2 ? "TRUE" : "FALSE") << endl;

   return 0;

 }


limits

Bibliothek numerischer Grenzen

Siehe Abschnitt numeric_limits in diesem Referenzteil.


list

Siehe auch	Container

Eine Sequenz, die bidirektionale Iteratoren unterstützt.

Syntax

#include <list>

template <class T>

 class list {

public:

// Typedefinitionen

   typedef typename iterator;

   typedef typename const_iterator;

   typedef typename reference;

   typedef typename const_reference;

   typedef typename size_type;

   typedef typename difference_type;

   typedef T value_type;

   typedef  reverse_iterator<iterator, value_type,

            reference, difference_type> reverse_iterator;

   typedef  const_reverse_iterator<const_iterator,

            value_type, reference,

            difference_type> const_reverse_iterator;

// Konstruieren/Kopieren/Entfernen

   explicit list ();

   explicit list (size_type n, const T& value = T());

   template <class InputIterator>

    list (InputIterator first, InputIterator last);

   list(const list<T>& x);

   ~list();

   list<T>& operator= (const list<T>&);

   template <class InputIterator>

    void assign (InputIterator first, InputIterator last);

   template <class Size, class T>

    void assign (Size n, const T& t = T());

// Iteratoren

   iterator begin ();

   const_iterator begin () const;

   iterator end ();

   const_iterator end () const;

   reverse_iterator rbegin ();

   const_reverse_iterator rbegin () const;

   reverse_iterator rend ();

   const_reverse_iterator rend () const;

// Kapazität

   bool empty () const;

   size_type size () const;

   size_type max_size () const;

   void resize (size_type sz, T c = T());

// Elementzugriff

   reference front ();

   const_reference front () const;

   reference back ();

   const_reference back () const;

// Modifizierer

   void push_front (const T& x);

   void pop_front ();

   void push_back (const T& x);

   void pop_back ();

   iterator insert (iterator position, const T& x = T());

   void insert (iterator position, size_type n, const T& x =

   T());

   template <class InputIterator>

    void insert  (iterator  position,  InputIterator  first,

   InputIterator last);

   void erase (iterator position);

   void erase (iterator position, iterator last);

   void swap (list<T>& x);

// Spezielle Umwandlungsoperationen für list

   void splice (iterator position, list<T>& x);

   void splice (iterator position, list<T>& x, iterator i);

   void splice (iterator position, list<T>& x,

               iterator first,iterator last);

   void remove (const T& value);

   template <class Predicate>

    void remove_if (Predicate pred);

   void unique ();

   template <class BinaryPredicate>

    void unique (BinaryPredicate binary_pred);

   void merge (list<T>& x);

   template <class Compare>

    void merge (list<T>& x, Compare comp);

   void sort ();

   template <class Compare>

    void sort (Compare comp);

   void reverse();

};

// Nicht-Element-List-Operatoren

template <class T>

 bool operator== (const list<T>&, const list<T>&);

template <class T>

 bool operator< (const list<T>&, const list<T>&);

Beschreibung

list<T> ist ein Sequenztyp, der bidirektionale Iteratoren unterstützt. list<T> ermöglicht Einfüge- und Löschoperationen an jeder Position der Sequenz in konstanter Ausführungszeit. Die Speicherverwaltung erfolgt dabei automatisch. Direkter Zugriff in konstanter Ausführungszeit wird nicht unterstützt.

Alle Typen, die für den Template-Parameter T verwendet werden, müssen folgendes zur Verfügung stellen (wobei T der type ist, t ein value von T ist und u ein const value von T ist):

  Standardkonstruktor    T().

  Kopierkonstruktoren    T(t) und T(u).

  Destruktor             t.~T().

  Adresse von            &t und &u, die T* bzw.

                          const T* zurückgeben.

  Zuweisung              t = a, wobei a ein

                          (möglicherweise const) value von T ist.

Beschränkungen

Elementfunktion-Templates werden in allen Containern verwendet, die die Standardbibliothek zur Verfügung stellt. Ein Beispiel hierfür ist der Konstruktor für list<T>, der zwei Template-Iteratoren annimmt:

template <class InputIterator>

 list (InputIterator, InputIterator);

list besitzt außerdem eine Funktion insert dieses Typs. Mit diesen Funktionen können Sie jeden Typ von Eingabeiterator als Argument verwenden, wenn keine Compiler-Einschränkungen vorliegen. Für Compiler, die diese Funktionen nicht unterstützen, gibt es Ersatzfunktionen, die es Ihnen ermöglichen, einen Iterator zu verwenden, der vom gleichen Containertyp stammt wie der zu konstruierende (bzw. für den eine Elementfunktion aufgerufen wird), oder Sie können mit einem Zeiger auf den Elementtyp arbeiten, den der Container enthält.

Wenn Ihr Compiler keine Elementfunktion-Templates unterstützt, können Sie eine Liste zum Beispiel auf die folgenden beiden Arten konstruieren:

int intarray[10];

list<int> first_list(intarray,intarray + 10);

list<int> second_list(first_list.begin(),first_list.end());

Dies ist jedoch nicht möglich:

list<long> long_list(first_list.begin(),first_list.end());

long_list und first_list sind nämlich nicht vom gleichen Typ.

Beispiel

 #include <list>

 #include <string>

 using namespace std;

 // Drucken einer Liste von Strings

 ostream& operator<<(ostream& out, const list<string>& l)

 {

   copy(l.begin(), l.end(), ostream_iterator<string>(cout," "));

   return out;

 }

 int main(void)

 {

   // Erzeugen einer Liste von Kreaturen

   list<string> critters;

   int i;

   // Kreaturen einfügen

   critters.insert(critters.begin(),"antelope");

   critters.insert(critters.begin(),"bear");

   critters.insert(critters.begin(),"cat");

   // Liste drucken

   cout << critters << endl;

   // Katze in Puma ändern

   *find(critters.begin(),critters.end(),"cat") = "cougar";

   cout << critters << endl;

   // Zebra an den Anfang setzen

   // einen Ozelot vor die Antilope

   // und eine Ratte an das Ende

   critters.push_front("zebra");

   critters.insert(find(critters.begin(),critters.end(),

                   "antelope"),"ocelot");

   critters.push_back("rat");

   cout << critters << endl;

   // Sortieren der Liste (Verwenden Sie die Funktion sort der Liste, da der

   // generische Algorithmus einen Random Access-Iterator benötigt

   // und list nur einen bidirektionalen Iterator besitzt)

   critters.sort();

   cout << critters << endl;

   // Nun löschen wir die Hälfte der Kreaturen

   int      half = critters.size() >> 1;

   for(i = 0; i < half; ++i) {

     critters.erase(critters.begin());

   }

   cout << critters << endl;

   return 0;

 }

Konstruktoren und Destruktoren

explicit list();

Erzeugt eine Liste mit null Elementen.

explicit list (size_type n, const T& value = T());

Erzeugt eine Liste der Länge n, die n Kopien von value enthält.

template <class InputIterator>

list (InputIterator first, InputIterator last);

Erzeugt eine Liste der Länge last - first, die mit allen Werten gefüllt ist, die sich durch das Dereferenzieren des InputIterators im Bereich [first, last) ergeben.

list (const list<T>& x);

Kopierkonstruktor. Erzeugt eine Kopie von x.

~list ();

Der Destruktor. Gibt den für diese Liste alloziierten Speicher frei.

Zuweisungsoperator

list<T>& operator= (const list<T>& x)

Zuweisungsoperator. Löscht alle Elemente in self und fügt anschließend in self eine Kopie jedes Elements in x ein. Gibt eine Referenz auf *this zurück.

Iteratoren

iterator begin ();

Gibt einen bidirektionalen Iterator zurück, der auf das erste Element zeigt.

const_iterator begin () const;

Gibt einen konstanten bidirektionalen Iterator zurück, der auf das erste Element zeigt.

iterator end ();

Gibt einen bidirektionalen Iterator zurück, der auf den Nach-Bereichsende-Wert zeigt.

const_iterator end () const;

Gibt einen konstanten bidirektionalen Iterator zurück, der auf den Nach-Bereichsende-Wert zeigt.

reverse_iterator rbegin ();

Gibt einen bidirektionalen Iterator zurück, der auf den Nach-Bereichsende-Wert zeigt.

const_reverse_iterator rbegin () const;

Gibt einen konstanten bidirektionalen Iterator zurück, der auf den Nach-Bereichsende-Wert zeigt.

reverse_iterator rend ();

Gibt einen bidirektionalen Iterator zurück, der auf das erste Element zeigt.

const_reverse_iterator rend () const;

Gibt einen konstanten bidirektionalen Iterator zurück, der auf das erste Element zeigt.

Elementfunktionen

template <class InputIterator>

void

assign (InputIterator first, InputIterator last);

Löscht alle in self enthaltenen Elemente und fügt anschließend neue Elemente aus dem Bereich [first, last) ein.

template <class Size, class T>

void

assign (Size n, const T& t = T());

Löscht alle in self enthaltenen Elemente und fügt anschließend n Instanzen des Werts von t ein.

reference

back ();

Gibt eine Referenz auf das letzte Element zurück.

const_reference

back () const;

Gibt eine konstante Referenz auf das letzte Element zurück.

bool

empty () const;

Gibt true zurück, wenn size Null ist.

void

erase (iterator position);

Entfernt das Element, auf das position zeigt.

void

erase (iterator first, iterator last);

Entfernt die Elemente im Bereich [first, last).

reference

front ();

Gibt eine Referenz auf das erste Element zurück.

const_reference

front () const;

Gibt eine konstante Referenz auf das erste Element zurück.

iterator

insert (iterator position, const T& x = T());

Fügt x vor position ein. Gibt einen Iterator zurück, der auf das eingefügte x zeigt.

void

insert (iterator position, size_type n, const  T& x = T());

Fügt n Kopien von x vor position ein.

template <class InputIterator>

void

insert (iterator position, InputIterator first,InputIterator last);

Fügt Kopien der Elemente im Bereich (first, last] vor position ein.

max_size () const;

Gibt size () der größten möglichen Liste zurück.

void merge (list<T>& x);

Führt ein sortiertes x mit einem sortierten self mit dem Operator < zusammen. Bei gleichen Elementen in den beiden Listen werden Elemente aus self den Elementen aus x immer vorangestellt. Die Funktion merge hinterläßt x leer.

template <class Compare>

void

merge (list<T>& x, Compare comp);

Führt ein sortiertes x mit einem sortierten self unter Verwendung eines Vergleichsfunktionsobjekts comp zusammen. Bei gleichen Elementen in den beiden Listen werden Elemente aus self den Elementen aus x immer vorangestellt. Die Funktion merge hinterläßt x leer.

void

pop_back ();

Entfernt das letzte Element.

void

pop_front ();

Entfernt das erste Element.

void

void push_back (const T& x);

Hängt eine Kopie von x am Ende an.

push_front (const T& x);

Hängt eine Kopie von x an den Anfang der Liste an.

void

remove (const T& value);

template <class Predicate>

void

remove_if (Predicate pred);

Entfernt alle Elemente in der Liste, die durch den Listeniterator i referenziert werden, für die *i == value bzw. pred(*i) == true ist. Es handelt sich um eine stabile Operation: Die relative Reihenfolge der Listenelemente, die nicht entfernt werden, bleibt erhalten.

void

resize (size_type sz, T c = T());

Ändert die Größe von self. Wenn die neue Größe ( sz ) größer als die aktuelle Größe ist, werden sz-size() cs am Ende der Liste eingefügt. Wenn die neue Größe kleiner als die aktuelle Kapazität ist, wird die Liste durch Löschen von size()-sz Elementen am Ende abgeschnitten. Wenn sz gleich der Kapazität ist, findet keine Aktion statt.

void

reverse ();

Kehrt die Reihenfolge der Elemente um.

size_type

size () const;

Gibt die Anzahl der Elemente zurück.

void

sort ();

Sortiert self mit operator<. sort bewahrt die relative Reihenfolge gleicher Elemente.

template <class Compare>

void

sort (Compare comp);

Sortiert self mit einem Vergleichsfunktionsobjekt comp. Die Sortierung ist stabil.

void

splice (iterator position, list<T>& x);

Fügt x vor position ein und hinterläßt x leer.

void

splice (iterator position, list<T>&  x,  iterator i);

Verschiebt die Elemente, auf die Iterator i in x zeigt, nach self und fügt sie vor position ein. Die Elemente werden aus x entfernt.

void

splice (iterator position, list<T>&  x,  iterator first, iterator last);

Verschiebt die Elemente im Bereich [first, last) in x nach self und fügt sie vor position ein. Die Elemente im Bereich [first, last) werden aus x entfernt.

void

swap (list<T>& x);

Vertauscht self und x.

void

unique ();

Löscht Kopien aufeinanderfolgender, wiederholter Elemente mit Ausnahme des ersten Vorkommens.

template <class BinaryPredicate>

void

unique (BinaryPredicate binary_pred);

Löscht aufeinanderfolgende Elemente, die einer Bedingung true des binary_pred genügen. Das erste Vorkommen wird nicht entfernt.

Nicht-Element-Operatoren

template <class T, class Allocator>

bool

operator== (const list<T>& x, const list <T>& y);

Gleichheitsoperator. Gibt true zurück, wenn x gleich y ist.

template <class T, class Allocator>

bool

operator< (const list<T>& x, const list <T>& y);

Gibt true zurück, wenn die durch die in x enthaltenen Elemente definierte Sequenz lexikographisch kleiner ist als die Sequenz, die durch die Elemente definiert wird, die in y enthalten sind.


logical_and

Funktionsobjekt

Binäres Funktionsobjekt, das true zurückgibt, wenn beide seiner Argumente true sind.

Syntax

#include <functional>

    template <class T>

    struct logical_and : binary_function<T, T, bool> {

         bool operator() (const T& x, const T& y) const

               { return x && y; }

    };

Beschreibung

logical_and ist ein binäres Funktionsobjekt. Sein operator() liefert true zurück, wenn sowohl x als auch y true sind. Ein Objekt logical_and kann an jeden Algorithmus übergeben werden, der eine binäre Funktion erfordert. Beispielsweise wendet der Algorithmus transform eine binäre Operation auf korrespondierende Werte in zwei Kollektionen an und speichert das Ergebnis der Funktion. logical_and wird in diesem Algorithmus in der folgenden Weise verwendet:

vector<bool> vec1;

vector<bool> vec2;

vector<bool> vecResult;

.

.

.

transform(vec1.begin(), vec1.end(),
          vec2.begin(), vec2.end(),
          vecResult.begin(), logical_and<bool>());

Nach diesem Aufruf von transform enthält vecResult(n) eine "1" (true), wenn sowohl vec1(n) als auch vec2(n) true sind oder eine "0" (false), wenn entweder vec1(n) oder vec2(n) false ist.


logical_not

Funktionsobjekt

Unäres Funktionsobjekt, das true zurückliefert, wenn sein Argument false ist.

Syntax

#include<functional>

    template <class T>

    struct logical_not : unary_function<T, T, bool> {

         bool operator() (const T& x, const T& y) const

               { return !x; }

    };

Beschreibung

logical_not ist ein unäres Funktionsobjekt. Sein operator() liefert true zurück, wenn sein Argument false ist. Ein Objekt logical_not kann an jeden Algorithmus übergeben werden, der ein unäres Funktionsobjekt erfordert. Beispielsweise ersetzt der Algorithmus replace_if ein Element durch einen anderen Wert, wenn das Ergebnis einer unären Operation true ist . logical_not wird in diesem Algorithmus in der folgenden Weise verwendet:

vector<int> vec1;

.

.

.

void replace_if(vec1.begin(), vec1.end(),
                logical_not<int>(),1);

Dieser Aufruf von replace_if ersetzt alle Nullen in vec1 durch "1".


logical_or

Funktionsobjekt

Binäres Funktionsobjekt, das true zurückgibt, wenn eines seiner Argumente true ist.

Syntax

#include<functional>

    template <class T>

    struct logical_or : binary_function<T, T, bool> {

         bool operator() (const T& x, const T& y) const

               { return x || y; }

    };

Beschreibung

logical_or ist ein binäres Funktionsobjekt. Sein operator() liefert true zurück, wenn entweder x oder y true ist. Ein Objekt vom Typ logical_or kann an jeden Algorithmus übergeben werden, der eine binäre Funktion erfordert. Beispielsweise wendet der Algorithmus transform eine binäre Operation auf korrespondierende Werte in zwei Kollektionen an und speichert das Ergebnis der Funktion. logical_or wird in diesem Algorithmus in der folgenden Weise verwendet:

vector<bool> vec1;

vector<bool> vec2;

vector<bool> vecResult;

.

.

.

transform(vec1.begin(), vec1.end(),
          vec2.begin(), vec2.end(),
          vecResult.begin(), logical_or<bool>());

Nach diesem Aufruf von transform enthält vecResult(n) eine "1" (true), wenn entweder vec1(n) oder vec2(n) true ist oder eine "0" (false), wenn sowohl vec1(n) als auch vec2(n) false sind.


lower_bound

Siehe auch	Algorithmus

Ermittelt die erste gültige Position für ein Element in einem sortierten Container.

Syntax

template <class ForwardIterator, class T>

    ForwardIterator

    lower_bound(ForwardIterator first, ForwardIterator last,

                const T& value);

 template <class ForwardIterator, class T, class Compare>

   ForwardIterator

   lower_bound(ForwardIterator first, ForwardIterator last,

                const T& value, Compare comp);

Beschreibung

Der Algorithmus lower_bound vergleicht einen angegebenen Wert value mit Elementen in einem sortierten Container und gibt die erste Position im Container zurück, die value einnehmen kann, ohne die Ordnung des Containers zu verletzen. Es gibt zwei Fassungen dieses Algorithmus. Die erste verwendet den Operator kleiner als (operator <) für den Vergleich und geht davon aus, daß der Container unter Verwendung dieses Operators sortiert wurde. Die zweite Fassung erlaubt die Angabe eines Funktionsobjekts vom Typ compare und geht davon aus, daß compare zugleich die für die Sortierung der Sequenz verwendete Funktion ist. Das Funktionsobjekt muß ein binäres Prädikat sein.

Der Rückgabewert von lower_bound ist der Iterator für das erste Element im Container, das größer als oder gleich value ist, oder, wenn der Vergleichsoperator verwendet wird, das erste Element, das der Vergleichsfunktion nicht genügt. Formal liefert der Algorithmus einen Iterator i im Bereich [first, last) zurück, so daß für jeden Iterator j in dem Bereich [first, i) die folgenden Bedingungen erfüllt sind:

*j  <  value

oder

 comp(*j,  value) == true

lower_bound führt höchstens log(last - first) + 1 Vergleiche aus.

Beispiel

 #include <vector>

 #include <algorithm>

 using namespace std;

 int main()

 {

   typedef vector<int>::iterator iterator;

   int d1[11] = {0,1,2,2,3,4,2,2,2,6,7};

   // Einen Vektor einrichten

   vector<int> v1(d1,d1 + 11);

   // Die Varianten von lower_bound ausprobieren

   iterator it1 = lower_bound(v1.begin(),v1.end(),3);

   // it1 = v1.begin() + 4

   iterator it2 =

       lower_bound(v1.begin(),v1.end(),2,less<int>());

   // it2 = v1.begin() + 4

   // Die Varianten von upper_bound ausprobieren

   iterator it3 = upper_bound(v1.begin(),v1.end(),3);

   // it3 = vector + 5

   iterator it4 =

      upper_bound(v1.begin(),v1.end(),2,less<int>());

   // it4 = v1.begin() + 5

   cout << endl << endl

        << "The upper and lower bounds of 3: ( "

        << *it1 << " , " << *it3 << " ]" << endl;

   cout << endl << endl

        << "The upper and lower bounds of 2: ( "

        << *it2 << " , " << *it4 << " ]" << endl;

   return 0;

 }


make_heap

Siehe auch	Algorithmus

Erzeugt einen Heap.

Syntax

#include <algorithm>

template <class RandomAccessIterator>

  void

  make_heap(RandomAccessIterator first,

            RandomAccessIterator last);

template <class RandomAccessIterator, class Compare>

  void

  make_heap(RandomAccessIterator first,

            RandomAccessIterator last, Compare comp);

Beschreibung

Ein Heap ist eine bestimmte Organisationsform von Elementen in einem Bereich zwischen zwei Random Access-Iteratoren [a, b). Seine zwei Hauptmerkmale sind:

1.	*a ist das größte Element im Bereich.

2.	*a kann mit dem Algorithmus pop_heap entfernt werden oder es kann ein neues Element mit dem Algorithmus push_heap hinzugefügt werden; dies geschieht in der Zeit O(logN).

Diese Eigenschaften machen Heaps als Ereigniswarteschlangen verwendbar.

Die Heap-Algorithmen verwenden den Operator kleiner als (<) als Standard-Vergleichsoperator. In allen Algorithmen kann ein alternativer Vergleichsoperator angegeben werden.

Die erste Fassung des Algorithmus make_heap ordnet die Elemente im Bereich [first, last) als Heap an und verwendet für die Durchführung des Vergleichs den Operator kleiner als (<). Die zweite Fassung benutzt den Vergleichsoperator comp für den Vergleich. Da die einzigen Bedingungen für einen Heap die zwei oben aufgeführten sind, braucht make_heap in dem Bereich (first, last-1) nicht unbedingt Aktionen auszuführen.

Dieser Algorithmus nimmt höchstens 3 * (last - first) Vergleiche vor.

Beispiel

 #include <algorithm>

 #include <vector>

 using namespace std;

 int main(void)

 {

   int d1[4] = {1,2,3,4};

   int d2[4] = {1,3,2,4};

   // Zwei Vektoren einrichten

   vector<int> v1(d1,d1 + 4), v2(d2,d2 + 4);

   // Heaps anlegen

   make_heap(v1.begin(),v1.end());

   make_heap(v2.begin(),v2.end(),less<int>());

   // v1 = (4,x,y,z)  und  v2 = (4,x,y,z)

   // Beachten Sie, daß x, y und z die verbleibenden

   // Werte in dem Container (also ungleich 4) darstellen.

   // Die Definition des Heaps und der Heap-Operationen

   // erfordert keine bestimmte Reihenfolge für

   // diese Werte.

   // Beide Vektoren nach cout kopieren

   ostream_iterator<int> out(cout," ");

   copy(v1.begin(),v1.end(),out);

   cout << endl;

   copy(v2.begin(),v2.end(),out);

   cout << endl;

   // Jetzt Werte entfernen (pop)

   pop_heap(v1.begin(),v1.end());

   pop_heap(v2.begin(),v2.end(),less<int>());

   // v1 = (3,x,y,4) and v2 = (3,x,y,4)

   // Beide Vektoren nach cout kopieren

   copy(v1.begin(),v1.end(),out);

   cout << endl;

   copy(v2.begin(),v2.end(),out);

   cout << endl;

   // Und nun einfügen (push)

   push_heap(v1.begin(),v1.end());

   push_heap(v2.begin(),v2.end(),less<int>());

   // v1 = (4,x,y,z) and v2 = (4,x,y,z)

   // Beide Vektoren nach cout kopieren

   copy(v1.begin(),v1.end(),out);

   cout << endl;

   copy(v2.begin(),v2.end(),out);

   cout << endl;

   // Jetzt die Heaps sortieren

   sort_heap(v1.begin(),v1.end());

   sort_heap(v2.begin(),v2.end(),less<int>());

   // v1 = v2 = (1,2,3,4)

   // Beide Vektoren nach cout kopieren

   copy(v1.begin(),v1.end(),out);

   cout << endl;

   copy(v2.begin(),v2.end(),out);

   cout << endl;

   return 0;

 }


map

Container

Ein assoziativer Container, der Zugriff auf nicht Indexwerte unter Verwendung eindeutiger Indizes (Schlüssel) bietet.

Syntax

#include <map>

template <class Key, class T, class Compare = less<Key> >

 class map {

public:

// Typen

   typedef Key key_type;

   typedef pair<const Key, T> value_type;

   typedef Compare key_compare;

   typedef typename reference;

   typedef typename const_reference;

   typedef typename iterator;

   typedef typename const_iterator;

   typedef typename size_type;

   typedef typename difference_type;

   typedef reverse_iterator<iterator,

                 value_type,

                 reference,

                 difference_type> reverse_iterator;

   typedef reverse_iterator<const_iterator,

                 value_type,

                 const_reference,

                 difference_type> const_reverse_iterator;

   class value_compare

      : public binary_function<value_type, value_type, bool>

   {

     friend class map;

     protected :

       Compare comp;

       value_compare(Compare c) : comp(c) {}

     public :

       bool operator() (const value_type& x,

                       const value_type& y) const

         { return comp(x.first, y.first); }

   };

// Konstruieren/Kopieren/Entfernen

   explicit map (const Compare& comp = Compare());

   template <class InputIterator>

    map (InputIterator first,

        InputIterator last,

        const Compare& comp = Compare());

   map (const map<Key, T, Compare>& x);

   ~map();

   map<Key, Compare>& operator= (const map<Key, T, Compare>&

   x);

// Iteratoren

   iterator begin();

   const_iterator begin() const;

   iterator end();

   const_iterator end() const;

   reverse_iterator rbegin();

   const_reverse_iterator rbegin() const;

   reverse_iterator rend();

   const_reverse_iterator rend() const;

// Kapazität

   bool empty() const;

   size_type size() const;

   size_type max_size() const;

// Elementzugriff

   T& operator[] (const key_type& x);

   const T& operator[] (const key_type& x) const;

// Modifizierer

   pair<iterator, bool> insert (const value_type& x);

   iterator insert (iterator position, const value_type& x);

   template <class InputIterator>

    void insert (InputIterator first, InputIterator last);

   void erase (iterator position);

   size_type erase (const key_type& x);

   void erase (iterator first, iterator last);

   void swap (map<Key, Compare>& x);

// Observer

   key_compare key_comp() const;

   value_compare value_comp() const;

// Map-Operationen

   iterator find (const key_value& x);

   const_iterator find (const key_value& x) const;

   size_type count (const key_type& x) const;

   iterator lower_bound (const key_type& x);

   const_iterator lower_bound (const key_type& x) const;

   iterator upper_bound (const key_type& x);

   const_iterator upper_bound (const key_type& x) const;

   pair<iterator, iterator> equal_range (const key_type& x);

   pair<const_iterator, const_iterator>

     equal_range (const key_type& x) const;

};

template <class Key,

          class T,

          class Compare>

 bool operator== (const map<Key, T, Compare>& x,

                 const map<Key, T, Compare>& y);

template <class Key,

          class T,

          class Compare>

 bool operator< (const map<Key, T, Compare>& x,

                const map<Key, T, Compare>& y);

Beschreibung

map bietet einen schnellen Zugriff auf gespeicherte Werte vom Typ T, die durch eindeutige Indizes (Schlüssel) eines eigenen Typs indiziert sind. Die Standardoperation für den Indexvergleich ist der Operator kleiner als (<).

map stellt bidirektionale Iteratoren zur Verfügung, die auf eine Instanz des Wertepaares pair<const Key x, T y> zeigen; dabei ist x der Index und y der gespeicherte Wert, der mit ihm verbunden ist. Die Definition von map verfügt über eine Typdefinition für ein solches Paar mit dem Namen value_type.

Die Typen, die für die beiden Template-Parameter Key und T verwendet werden, müssen den folgenden Bedingungen genügen (dabei ist T der Typ, t ein Wert value von T und u ein const value von T):

  Kopierkonstruktoren -  T(t) und T(u)

  Destruktor          -  t.~T()

  Adresse von         -  &t und &u, was T* beziehungsweise

                         const T* ergibt

  Zuweisung           -  t = a; dabei ist a ein

                         (unter Umständen const-) value von T

Der Typ, der für den Template-Parameter Compare verwendet wird, muß den Bedingungen für binäre Funktionen genügen.

Beschränkungen

In allen Containern der Standardbibliothek werden Elementfunktions-Templates verwendet. Ein Beispiel für dieses Merkmal ist der Konstruktor für map<Key,Compare>, der zwei Iterator-Templates annimmt:

template <class InputIterator>

 map (InputIterator, InputIterator, Compare);

map verfügt darüber hinaus über eine Einfügefunktion dieses Typs. Diese Funktionen erlauben - sofern Compiler-Beschränkungen das nicht verhindern - die Verwendung jedes Typs von Eingabeiteratoren als Argument. Für Compiler, die dieses Merkmal nicht unterstützen, stehen Ersatzfunktionen zur Verfügung, die es erlauben, einen Iterator zu verwenden, der aus dem gleichen Container-Typ wie dem zu konstruierenden (oder demjenigen, für den eine Elementfunktion aufgerufen wird) stammt. Alternativ kann ein Zeiger auf den im Container enthaltenen Elementtyp verwendet werden.

Wenn der verwendete Compiler keine Templates für Elementfunktionen unterstützt, kann eine map beispielsweise auf eine der beiden folgenden Weisen konstruiert werden:

map<int,int,less<int> >::value_type intarray[10];

map<int,int,less<int> > first_map(intarray,intarray + 10);

map<int, less<int> >

second_map(first_map.begin(),first_map.end());

Aber nicht auf die folgende Weise:

map<long, long,less<long> >

long_map(first_map.begin(),first_map.end());

Denn long_map und first_map sind nicht vom gleichen Typ.

Ferner unterstützen viele Compiler keine voreingestellten Template-Argumente. Wenn das auch auf den verwendeten Compiler zutrifft, muß stets das Template-Argument Compare angegeben werden.

Beispiel

 #include <string>

 #include <map>

 using namespace std;

 typedef map<string, int, less<string> > months_type;

 // Ein Paar ausgeben

 template <class First, class Second>

 ostream& operator<<(ostream& out,

                     const pair<First,Second> & p)

 {

   cout << p.first << " has " << p.second << " days";

   return out;

 }

 // Eine Map ausgeben

 ostream& operator<<(ostream& out, const months_type & l)

 {

   copy(l.begin(),l.end(), ostream_iterator

                <months_type::value_type>(cout,"\n"));

   return out;

 }

 int main(void)

 {

   // eine Map mit Monaten und der Anzahl der Tage

   // pro Monat erzeugen

   months_type months;

   typedef months_type::value_type value_type;

   // Die Monate in die Multimap einsetzen

   months.insert(value_type(string("January"),   31));

   months.insert(value_type(string("February"),   28));

   months.insert(value_type(string("February"),   29));

   months.insert(value_type(string("March"),     31));

   months.insert(value_type(string("April"),     30));

   months.insert(value_type(string("May"),       31));

   months.insert(value_type(string("June"),      30));

   months.insert(value_type(string("July"),      31));

   months.insert(value_type(string("August"),    31));

   months.insert(value_type(string("September"), 30));

   months.insert(value_type(string("October"),   31));

   months.insert(value_type(string("November"),  30));

   months.insert(value_type(string("December"),  31));

   // die Monate ausgeben

   // Second Febuary is not present

   cout << months << endl;

   // Die Anzahl Tage in Juni (June) ermitteln

   months_type::iterator p = months.find(string("June"));

   // die Anzahl Tage in Juni ausgeben

   if (p != months.end())

     cout << endl << *p << endl;

   return 0;

 }

Konstruktoren und Destruktoren

explicit map (const Compare& comp = Compare());

Standardkonstruktor. Konstruiert eine leere Map, die die Relation Compare zum Ordnen der Indizes verwendet, wenn sie angegeben wird.

template <class InputIterator>

map (InputIterator first, InputIterator last,

     const Compare& comp = Compare());

Konstruiert eine Map, die Werte in dem Bereich [first, last) enthält. Die Erzeugung der neuen Map funktioniert nur dann mit Sicherheit, wenn die Iteratoren first und last Werte vom Typ pair<class Key, class Value> zurückgeben, und alle Werte von Key in dem Bereich [first, last) eindeutig sind.

map (const map<Key, T, Compare>& x);

Kopierkonstruktor. Erzeugt eine neue Map, indem er alle Wertpaare key und value aus x kopiert.

~map ();

Destruktor. Gibt jeglichen für diese Map alloziierten Speicher frei.

Iteratoren

iterator begin() ;

Liefert einen Iterator zurück, der auf das erste in der Map gespeicherte Element zeigt. Das "erste" ist durch den Vergleichsoperator der Map, Compare, definiert.

const_iterator begin() const;

Liefert einen const_iterator zurück, der auf das erste in der Map gespeicherte Element zeigt.

iterator end() ;

Liefert einen Iterator zurück, der auf das letzte in der Map gespeicherte Element zeigt, also auf den Nach-Bereichsende-Wert.

const_iterator end() const;

Liefert einen const_iterator zurück, der auf das letzte in der Map gespeicherte Element zeigt.

reverse_iterator rbegin() ;

Liefert einen reverse_iterator zurück, der auf das erste in der Map gespeicherte Element zeigt. Das "erste" ist durch den Vergleichsoperator der Map, Compare, definiert.

const_reverse_iterator rbegin() const;

Liefert einen const_reverse_iterator zurück, der auf das erste in der Map gespeicherte Element zeigt.

reverse_iterator rend() ;

Liefert einen reverse_iterator zurück, der auf das letzte in der Map gespeicherte Element zeigt, also auf den Nach-Bereichsende-Wert.

const_reverse_iterator rend() const;

Liefert einen const_reverse_iterator zurück, der auf das letzte in der Map gespeicherte Element zeigt.

Elementoperatoren

map<Key, T, Compare>&

operator= (const map<Key, T, Compare>& x);

Zuweisung. Ersetzt den Inhalt von *this durch eine Kopie der Map x.

T& operator[] (const key_type& x);

Wenn ein Element mit dem Index x in der Map vorhanden ist, dann wird eine Referenz auf den mit ihm verbundenen Wert zurückgegeben. Andernfalls wird das Paar x,T() in die Map eingesetzt und eine Referenz auf das Standardobjekt T() zurückgegeben.

Elementfunktionen

size_type

count (const key_type& x) const;

Liefert eine 1 zurück, wenn ein Wert mit dem Index x in der Map vorhanden ist; andernfalls wird eine 0 zurückgegeben.

bool

empty() const;

Gibt true zurück, wenn die Map leer ist, andernfalls false.

pair<iterator,iterator>

equal_range  (const  key_type& x)

Liefert das Paar (lower_bound(x), upper_bound(x)) zurück.

pair<const_iterator,const_iterator>

equal_range (const key_type& x) const;

Liefert das Paar (lower_bound(x), upper_bound(x)) zurück.

void

erase (iterator position);

Löscht das Element aus der Map, auf das der Iterator position zeigt.

size_type

erase (const key_type& x);

Löscht das Element mit dem Indexwert x aus der Map, wenn es vorhanden ist.

void

erase (iterator first, iterator last);

Vorausgesetzt, daß die Iteratoren first und last auf die gleiche Map zeigen und last von first aus erreichbar ist, werden alle Elemente in dem Bereich [first, last) aus der Map gelöscht.

iterator

find (const key_type& x);

Durchsucht die Map nach einem Paar mit dem Indexwert x und liefert einen Iterator auf dieses Paar zurück, wenn es gefunden wird. Wenn kein entsprechendes Paar gefunden wird, wird der Wert end() zurückgegeben.

const_iterator find (const key_type& x) const;

Gleiche Funktion wie find oben, es wird jedoch ein const_iterator zurückgegeben.

pair<iterator, bool>

insert (const value_type& x);

iterator

insert (iterator position, const  value_type& x);

Wenn in der Map nicht bereits ein value_type mit dem gleichen Index wie x vorhanden ist, dann wird x in die Map eingefügt. Es kann eine Position für die Einfügung vorgeschlagen werden. Wenn die Einfügung direkt hinter position durchgeführt werden kann, dann wird dieser Vorgang mit konstanter Ausführungszeit durchgeführt. Andernfalls beansprucht der die Zeit O(log N).

template <class InputIterator>

void

insert (InputIterator first, InputIterator last);

Es werden Kopien von jedem Element in dem Bereich [first, last) in die Map eingefügt. Der Bereich muß über einen eindeutigen Index verfügen, und die entsprechenden Indexwerte dürfen in der Map noch nicht vorhanden sein. Die Iteratoren first und last müssen Werte vom Typ pair<T1,T2> zurückliefern. Dieser Vorgang beansprucht ungefähr die Ausführungszeit O(N*log(size()+N)).

key_compare

key_comp () const;

Liefert ein Funktionsobjekt zurück, das die Indexwerte mittels der Vergleichsfunktion Compare der aktuellen Map vergleichen kann.

iterator

lower_bound (const key_type& x)

Liefert einen Iterator auf die kleinsten Elemente der Map zurück, deren Index größer oder gleich x ist. Wenn kein solches Element existiert, wird end() zurückgegeben.

const_iterator

lower_bound (const key_type& x) const;

Gleiche Funktion wie lower_bound oben, es wird jedoch ein const_iterator zurückgegeben.

size_type

max_size() const;

Liefert die maximal mögliche Größe der Map zurück. Diese Größe ist nur durch die Anzahl der eindeutigen Schlüssel (Indizes) beschränkt, die von dem Typ Key dargestellt werden können.

size_type

size() const;

Liefert die Anzahl der Elemente in der Map zurück.

void swap (map<Key, T, Compare>& x);

Tauscht den Inhalt der Map x gegen den der aktuellen Map *this aus.

iterator

upper_bound (const key_type& x)

Liefert einen Iterator auf das größte Element der Map zurück, dessen Index kleiner oder gleich x ist. Wenn kein solches Element existiert, wird end() zurückgegeben.

const_iterator

upper_bound (const key_type& x) const;

Gleiche Funktion wie upper_bound oben, jedoch wird ein const_iterator zurückgegeben.

value_compare

value_comp () const;

Liefert ein Funktionsobjekt zurück, das Indexwerte mit dem Vergleichsoperator der aktuellen Map Compare vergleichen kann. Diese Funktion ist identisch mit der Funktion key_comp für Mengen.

Nicht-Element-Operatoren

bool operator== (const map<Key, T, Compare>& x,

const map<Key, T, Compare>& y);

Liefert true zurück, wenn alle Elemente in x elementweise mit allen Elementen in y übereinstimmen; dazu wird (T::operator==) verwendet. Andernfalls wird false zurückgegeben.

bool operator< (const map<Key, T, Compare>& x,

                const map<Key, T, Compare>& y);

Liefert true zurück, wenn x lexikalisch weniger ist als y. Andernfalls wird false zurückgegeben.


max

Siehe auch	Algorithmus

Sucht das Maximum in einem Wertepaar und liefert es zurück.

Syntax

#include <algorithm>

template <class T>

 const T& max(const T& a, const T& b);

template <class T, class Compare>

 const T& max(const T& a, const T& b, Compare comp);

Beschreibung

Der Algorithmus max ermittelt das Maximum in einem Wertepaar und liefert es zurück. Das optionale Argument comp definiert eine Vergleichsfunktion, die anstelle des Standardoperators < verwendet werden kann. Diese Funktion kann in Verbindung mit allen Datentypen verwendet werden, die in der Standardbibliothek zur Verfügung stehen.

Wenn die Argumente gleich sind, gibt max das erste zurück.

Beispiel

 #include <algorithm>

 using namespace std;

 int main(void)

 {

   double  d1 = 10.0, d2 = 20.0;

   // Minimum suchen

   double val1 = min(d1, d2);

   // val1 = 10.0

   // der Größer-Komparator gibt den größeren der beiden Werte

   // zurück.

   double val2 = min(d1, d2, greater<double>());

   // val2 = 20.0;

   // Maximum suchen

   double val3 = max(d1, d2);

   // val3 = 20.0;

   // der Kleiner-Komparator gibt den kleineren der beiden Werte zurück.

   // Beachten Sie, daß max, wie alle Vergleiche in der STL, mittels des

   // Operators < ausgedrückt ist, so daß die Verwendung von kleiner an

   // dieser Stelle das gleiche bewirkt wie die Verwendung des Algorithmus

   // max mit einem Standardkomparator.

   double val4 = max(d1, d2, less<double>());

   // val4 = 20

   cout << val1 << " " << val2 << " "

        << val3 << " " << val4 << endl;

   return 0;

 }


max_element

Siehe auch	Algorithmus

Sucht den Maximalwert in einem Bereich.

Syntax

#include <algorithm>

template <class ForwardIterator>

 InputIterator

 max_element(ForwardIterator first, ForwardIterator last);

template <class ForwardIterator, class Compare>

 InputIterator

 max_element(ForwardIterator first, ForwardIterator last,

               Compare comp);

Beschreibung

Der Algorithmus max_element liefert einen Iterator zurück, der das größte Element in einer Sequenz bezeichnet. Wenn die Sequenz mehr als ein Exemplar dieses Elements enthält, zeigt der Iterator auf dessen erstes Auftreten. Das optionale Argument comp definiert eine Vergleichsfunktion, die anstelle des voreingestellten Operators kleiner als (<) verwendet werden kann. Diese Funktion kann in Verbindung mit allen Datentypen eingesetzt werden, die in der Standardbibliothek zur Verfügung stehen.

Der Algorithmus max_element liefert den ersten Iterator i in dem Bereich [first, last)zurück, so daß für jeden Iterator j in dem gleichen Bereich die folgenden Bedingungen erfüllt sind:

!(*i < *j)

oder

comp(*i, *j) == false.

Für max_element werden exakt max((last - first) - 1, 0) Anwendungen der entsprechenden Vergleichsoperationen durchgeführt.

Beispiel

 #include <algorithm>

 #include <vector>

 using namespace std;

 int main(void)

 {

   typedef vector<int>::iterator iterator;

   int d1[5] = {1,3,5,32,64};

   // Vektor einrichten

   vector<int>      v1(d1,d1 + 5);

   // größtes Element in dem Vektor suchen

   iterator it1 = max_element(v1.begin(), v1.end());

   // it1 = v1.begin() + 4

   // größtes Element in dem Bereich vom Anfang des Vektors

   // bis zu dem vorletzten Element suchen

   iterator it2 = max_element(v1.begin(), v1.end()-1,

                      less<int>());

   // it2 = v1.begin() + 3

   // kleinstes Element suchen

   iterator it3 = min_element(v1.begin(), v1.end());

   // it3 = v1.begin()

   // kleinsten Wert in dem Bereich vom Anfang des Vektors plus 1

   // bis zum Ende suchen

   iterator it4 = min_element(v1.begin()+1, v1.end(),

                      less<int>());

   // it4 = v1.begin() + 1

   cout << *it1 << " " << *it2 << " "

        << *it3 << " " << *it4 << endl;

   return 0;

 }


merge

Algorithmus

Zwei sortierte Sequenzen zu einer dritten zusammenführen.

Syntax

#include <algorithm>

template <class InputIterator1, class InputIterator2,

          class OutputIterator>

  OutputIterator

  merge(InputIterator first1, InputIterator1 last1,

        InputIterator2 first2, InputIterator last2,

        OutputIterator result);

template <class InputIterator1, class InputIterator2,

          class OutputIterator, class Compare>

  OutputIterator

  merge(InputIterator1 first1, InputIterator1 last1,

        InputIterator2 first2, InputIterator last2,

        OutputIterator result, Compare comp);

Beschreibung

Der Algorithmus merge führt zwei sortierte Sequenzen, die durch [first1, last1) und [first2, last2) bezeichnet sind, in der in [result, result + (last1 - first1) + (last2 - first2)) angegebenen Sequenz zusammen. Die erste Fassung des Algorithmus merge verwendet den Operator kleiner als (<), um Elemente in den beiden Sequenzen zu vergleichen. Die zweite Fassung verwendet die durch den Funktionsaufruf zur Verfügung gestellte Vergleichsfunktion. Wenn eine Vergleichsfunktion zur Verfügung gestellt wird, nimmt merge an, daß beide Sequenzen mit dieser Vergleichsfunktion sortiert worden sind.

Die Zusammenführungsoperation ist stabil. Das heißt, wenn die zwei Ausgangssequenzen äquivalente Elemente enthalten, stehen in der sich ergebenden Sequenz die Elemente aus der ersten Ausgangssequenz immer vor den entsprechenden Elementen aus der zweiten. Die Größe des Resultats einer Operation des Typs merge ist gleich der Summe der Größen der beiden Argumentsequenzen. merge liefert einen Iterator zurück, der auf das Ende der resultierenden Sequenz zeigt, das heißt also result + (last1 - first1) + (last2 -first2). Das Ergebnis von merge ist dann nicht definiert, wenn der resultierende Bereich sich mit einem der Originalbereiche überschneidet.

merge geht davon aus, daß zumindest (last1 - first1) + (last2 - first2) Elemente auf result folgen, es sei denn, result wurde durch einen Einfügeiterator adaptiert.

Für merge werden höchstens (last - first1) + (last2 - first2) - 1 Vergleichsoperationen durchgeführt.

Beispiel

 #include <algorithm>

 #include <vector>

 using namespace std;

 int main()

 {

   int d1[4] = {1,2,3,4};

   int d2[8] = {11,13,15,17,12,14,16,18};

   // Zwei Vektoren einrichten

   vector<int> v1(d1,d1 + 4), v2(d1,d1 + 4);

   // Vier Zielvektoren einrichten

   vector<int> v3(d2,d2 + 8),v4(d2,d2 + 8),

               v5(d2,d2 + 8),v6(d2,d2 + 8);

   // Einen leeren Vektor einrichten

   vector<int> v7;

   // v1 mit v2 zusammenführen

   merge(v1.begin(),v1.end(),v2.begin(),v2.end(),v3.begin());

   // Jetzt Komparator verwenden

   merge(v1.begin(),v1.end(),v2.begin(),v2.end(),v4.begin(),
         less<int>());

   // Stationäres Zusammenführen v5 (inplace)

   vector<int>::iterator mid = v5.begin();

   advance(mid,4);

   inplace_merge(v5.begin(),mid,v5.end());

   // Jetzt einen Komparator auf v6 anwenden

   mid = v6.begin();

   advance(mid,4);

   inplace_merge(v6.begin(),mid,v6.end(),less<int>());

   // v1 und v2 in leerem Vektor zusammenführen
 // und dazu Einfügeiterator verwenden

   merge(v1.begin(),v1.end(),v2.begin(),v2.end(),

         back_inserter(v7));

   // Alles nach cout kopieren

   ostream_iterator<int> out(cout," ");

   copy(v1.begin(),v1.end(),out);

   cout << endl;

   copy(v2.begin(),v2.end(),out);

   cout << endl;

   copy(v3.begin(),v3.end(),out);

   cout << endl;

   copy(v4.begin(),v4.end(),out);

   cout << endl;

   copy(v5.begin(),v5.end(),out);

   cout << endl;

   copy(v6.begin(),v6.end(),out);

   cout << endl;

   copy(v7.begin(),v7.end(),out);

   cout << endl;

   // v1 und v2 in cout zusammenführen

   merge(v1.begin(),v1.end(),v2.begin(),v2.end(),

         ostream_iterator<int>(cout," "));

   cout << endl;

   return 0;

 }


min

Siehe auch	Algorithmus

Sucht das Minimum eines Wertepaars und liefert es zurück.

Syntax

#include <algorithm>

template <class T>

 const T& min(const T& a, const T& b);

template <class T, class Compare>

 const T& min(const T& a, const T& b, Compare comp);

Beschreibung

Der Algorithmus min ermittelt das Minimum in einem Wertepaar und liefert es zurück. In der zweiten Fassung des Algorithmus definiert das optionale Argument comp eine Vergleichsfunktion, die anstelle des Standardoperators < verwendet werden kann. Diese Funktion kann in Verbindung mit allen Datentypen verwendet werden, die in der Standardbibliothek zur Verfügung stehen.

Wenn die beiden Argumente gleich sind, gibt min das erste zurück.

Beispiel

 #include <algorithm>

 using namespace std;

 int main(void)

 {

   double  d1 = 10.0, d2 = 20.0;

   // Minimum suchen

   double val1 = min(d1, d2);

   // val1 = 10.0

   // der Größer-Komparator liefert den größeren der beiden

   // Werte zurück.

   double val2 = min(d1, d2, greater<double>());

   // val2 = 20.0;

   // Maximum suchen

   double val3 = max(d1, d2);

   // val3 = 20.0;

   // der Kleiner-Komparator liefert den kleineren der beiden

   // Werte zurück.

   // Beachten Sie, daß max, wie jede Vergleichsfunktion in der STL,

   // mittels des Operators < definiert ist, so daß die Verwendung von

   // Kleiner in diesem Beispiel gleichbedeutend mit dem voreingestellten

   // Komparator ist.

   double val4 = max(d1, d2, less<double>());

   // val4 = 20

   cout << val1 << " " << val2 << " "

        << val3 << " " << val4 << endl;

   return 0;

 }


min_element

Siehe auch	Algorithmus

Sucht den Minimalwert in einem Bereich.

Syntax

#include <algorithm>

template <class ForwardIterator>

 ForwardIterator

 min_element(ForwardIterator first, ForwardIterator last);

template <class ForwardIterator, class Compare>

 InputIterator

 min_element(ForwardIterator first, ForwardIterator last,

             Compare comp);

Beschreibung

Der Algorithmus min_element liefert einen Iterator zurück, der das kleinste Element in einer Sequenz bezeichnet. Wenn die Sequenz mehr als ein Exemplar dieses kleinsten Elements enthält, zeigt der Iterator auf dessen erste Instanz. In der zweiten Fassung der Funktion definiert das optionale Argument comp eine Vergleichsfunktion, die anstelle des voreingestellten Operators kleiner als (<) verwendet werden kann. Diese Funktion kann zusammen mit allen in der Standardbibliothek zur Verfügung stehenden Datentypen verwendet werden.

Der Algorithmus min_element liefert den ersten Iterator i in dem Bereich [first, last) zurück, so daß für jeden Iterator j in dem gleichen Bereich die folgenden Bedingungen erfüllt sind:

 !(*j < *i)

oder

comp(*j, *i) == false.

min_element führt exakt max((last - first) - 1, 0) Anwendungen der entsprechenden Vergleichsoperationen durch.

Beispiel

 #include <algorithm>

 #include <vector>

 using namespace std;

 int main(void)

 {

   typedef vector<int>::iterator iterator;

   int d1[5] = {1,3,5,32,64};

   // Vektor einrichten

   vector<int>      v1(d1,d1 + 5);

   // größtes Element im Vektor suchen

   iterator it1 = max_element(v1.begin(), v1.end());

   // it1 = v1.begin() + 4

   // das größte Element in dem Bereich vom Anfang

   // des Vektors bis zum vorletzten Element suchen

   iterator it2 = max_element(v1.begin(), v1.end()-1,

                      less<int>());

   // it2 = v1.begin() + 3

   // kleinstes Element suchen

   iterator it3 = min_element(v1.begin(), v1.end());

   // it3 = v1.begin()

   // kleinsten Wert in dem Bereich vom Anfang des Vektors

   // plus 1 bis zum Ende suchen

   iterator it4 = min_element(v1.begin()+1, v1.end(),

                      less<int>());

   // it4 = v1.begin() + 1

   cout << *it1 << " " << *it2 << " "

        << *it3 << " " << *it4 << endl;

   return 0;

 }


minus

Funktionsobjekt

Liefert das Ergebnis des Subtraktion des zweiten Arguments vom ersten zurück.

Syntax

#include<functional>

   template <class T>

   struct minus : binary_function<T, T, T> {

        T operator() (const T& x, const T& y) const

              { return x - y; }

   };

Beschreibung

minus ist ein binäres Funktionsobjekt. Sein operator() gibt das Ergebnis von x minus y zurück. Ein Objekt des Typs minus kann an jeden Algorithmus übergeben werden, der eine binäre Funktion annimmt. Beispielsweise wendet der Algorithmus transform eine binäre Operation auf die korrespondierenden Werte in zwei Kollektionen an und speichert das Ergebnis. minus würde in diesem Algorithmus in der folgenden Weise verwendet:

vector<int> vec1;

vector<int> vec2;

vector<int> vecResult;

.

.

.

transform(vec1.begin(), vec1.end(),
          vec2.begin(), vec2.end(),
          vecResult.begin(), minus<int>());

Nach diesem Aufruf von transform enthält vecResult(n) vec1(n) minus vec2(n).


mismatch

Algorithmus

Vergleicht Elemente aus zwei Sequenzen und liefert die ersten beiden Elemente zurück, die nicht übereinstimmen.

Syntax

#include <algorithm>

template <class InputIterator1, class InputIterator2>

  pair<InputIterator1,InputIterator2>

  mismatch(InputIterator1 first1, InputIterator1 last1,

           InputIterator2 first2);

template <class InputIterator1, class InputIterator2,

          class BinaryPredicate>

  pair<InputIterator1, Inputiterator2>

  mismatch(InputIterator first1, InputIterator1 last1,

           InputIterator2 first2,

           BinaryPredicate binary_pred);

Beschreibung

Der Algorithmus mismatch vergleicht die Elemente zweier Sequenzen und liefert zwei Iteratoren (i und j) zurück, die auf die erste unterschiedliche Position in jeder der beiden Sequenzen zeigen. Beachten Sie, daß der Algorithmus für die erste Sequenz sowohl eine Anfangs- als auch eine Endposition bezeichnet, für die zweite Sequenz jedoch nur eine Anfangsposition. mismatch geht davon aus, daß die zweite Sequenz wenigstens so viele Elemente enthält wie die erste Sequenz. Wenn die beiden Sequenzen identisch sind, gibt mismatch ein Iteratorenpaar zurück, das auf das Ende der ersten Sequenz und auf die entsprechende Stelle in der zweiten Sequenz zeigt, an der der Vergleich beendet war.

Die erste Fassung von mismatch prüft die Elemente einer Sequenz auf Gleichheit, während die zweite Fassung die Angabe einer Vergleichsfunktion erlaubt. Die Vergleichsfunktion muß ein binäres Prädikat sein.

Die Iteratoren i und j, die von mismatch zurückgegeben werden, sind wie folgt definiert:

j  == first2  +  (i  -  first1)

und i ist der erste Iterator im Bereich [first1, last1) für den die entsprechende der beiden folgenden Bedingungen zutrifft:

!(*i  ==  *(first2  +  (i  -  first1)))

oder

binary_pred(*i, *(first2 + (i - first1))) == false

Wenn alle Elemente in den beiden Sequenzen übereinstimmen, gibt mismatch ein Paar von last1 und first2 + (last1 - first1) zurück.

Es werden höchstens last1 - first1 Anwendungen des entsprechenden Prädikats ausgeführt.

Beispiel

 #include <algorithm>

 #include <vector>

 using namespace std;

 int main(void)

 {

   typedef vector<int>::iterator  iterator;

   int d1[4] = {1,2,3,4};

   int d2[4] = {1,3,2,4};

   // Zwei Vektoren einrichten

   vector<int> vi1(d1,d1 + 4), vi2(d2,d2 + 4);

   // p1 enthält zwei Iteratoren, die auf das erste

   // Elementpaar zeigen, das in den beiden Vektoren

   // unterschiedlich ist

   pair<iterator, iterator> p1 = mismatch(vi1.begin(), vi1.end(),

                                         vi2.begin());

   // die ersten beiden Elemente suchen, für die gilt, daß ein

   // Element im ersten Vektor größer ist als das Element in dem

   // zweiten Vektor.

   pair<iterator, iterator> p2 = mismatch(vi1.begin(), vi1.end(),

                                        vi2.begin(),

                                        less_equal<int>());

   // Ergebnisse ausgeben

   cout << *p1.first << ", " << *p1.second << endl;

   cout << *p2.first << ", " << *p2.second << endl;

   return 0;

 }


modulus

Funktionsobjekt

Liefert den Rest, der sich bei der Division des ersten Arguments durch das zweite Argument ergibt.

Syntax

#include<functional>

   template <class T>

   struct modulus : binary_function<T, T, T> {

        T operator() (const T& x, const T& y) const

              { return x % y; }

   };

Beschreibung

modulus ist ein binäres Funktionsobjekt. Sein operator() liefert den Rest zurück, der sich bei x geteilt durch y ergibt. Ein Objekt des Typs modulus kann an jeden Algorithmus, der eine binäre Funktion annimmt, übergeben werden. Beispielsweise wendet der Algorithmus transform eine binäre Operation auf die korrespondierenden Werte in zwei Kollektionen an und speichert das Ergebnis. modulus würde in diesem Algorithmus in der folgenden Weise verwendet:

vector<int> vec1;

vector<int> vec2;

vector<int> vecResult;

.

.

.

transform(vec1.begin(), vec1.end(),
          vec2.begin(), vec2.end(),
          vecResult.begin(), modulus<int>());

Nach diesem Aufruf von transform enthält vecResult(n) den Rest aus vec1(n) geteilt durch vec2(n).


multimap

Container

Ein assoziativer Container, der Zugriff auf Nicht-Indexwerte über Indizes bietet. multimap-Indizes (Schlüssel) müssen nicht eindeutig sein.

Syntax

#include <map>

template <class Key, class T, class Compare = less<Key> >

 class multimap {

public:

// Typen

   typedef Key key_type;

   typedef pair<const Key, T> value_type;

   typedef Compare key_compare;

   typedef typename reference;

   typedef typename const_reference;

   typedef typename iterator;

   typedef typename const_iterator;

   typedef typename size_type;

   typedef typename difference_type;

   typedef reverse_iterator<iterator,

                value_type,

                reference,

                difference_type> reverse_iterator;

   typedef reverse_iterator<const_iterator,

                value_type,

                const_reference,

                difference_type> const_reverse_iterator;

   class value_compare

      : public binary_function<value_type, value_type, bool>

// Konstruieren/Kopieren/Entfernen

   explicit multimap (const Compare& comp = Compare());

   template <class InputIterator>

    multimap (InputIterator first,

        InputIterator last,

        const Compare& comp = Compare());

   multimap (const multimap<Key, T, Compare>& x);

   ~multimap ();

multimap<Key, T, Compare>& operator=

       const multimap<Key, T, Compare>& x);

// Iteratoren

   iterator begin ();

   const_iterator begin () const;

   iterator end ();

   const_iterator end () const;

   reverse_iterator rbegin ();

   const_reverse_iterator rbegin () const;

   reverse_iterator rend ();

   const_reverse_iterator rend () const;

// Kapazität

   bool empty () const;

   size_type size () const;

   size_type max_size () const;

// Modifizierer

   iterator insert (const value_type& x);

   iterator insert (iterator position, const value_type& x);

   template <class InputIterator>

    void insert (InputIterator first, InputIterator last);

   void erase (iterator position);

   size_type erase (const key_type& x);

   void erase (iterator first, iterator last);

   void swap (multimap<Key, T, Compare>& x);

// Observer

   key_compare key_comp () const;

   value_compare value_comp () const;

// Multimap-Operationen

   iterator find (const key_value& x);

   const_iterator find (const key_value& x) const;

   size_type count (const key_type& x) const;

   iterator lower_bound (const key_type& x);

   const_iterator lower_bound (const key_type& x) const;

   iterator upper_bound (const key_type& x);

   const_iterator upper_bound (const key_type& x) const;

   pair<iterator, iterator> equal_range (const key_type& x);

   pair<const_iterator, const_iterator>

     equal_range (const key_type& x) const;

};

template <class Key,class T,class Compare>

 bool operator==

    (const multimap<Key, T, Compare>& x,

     const multimap<Key, T, Compare>& y);

template <class Key,

          class T,

          class Compare>

 bool operator<

    (const multimap<Key, T, Compare>& x,

     const multimap<Key, T, Compare>& y);

Beschreibung

multimap erlaubt den schnellen Zugriff auf gespeicherte Werte vom Typ T, die durch Indizes (Schlüssel) von einem anderen Typ indiziert sind. Die Standardoperation für Indexvergleiche ist der der Operator <. Im Gegensatz zu map erlaubt multimap die Einfügung mehrfach vorhandener Indizes.

multimap stellt bidirektionale Iteratoren zur Verfügung, die auf eine Instanz vom Typ pair<const Key x, T y> zeigen; dabei ist x der Index und y der dazu gehörende gespeicherte Wert. In der Definition von multimap findet sich eine typedef zum Datentyp pair mit dem Namen value_type.

Die Typen, die für die Template-Parameter Key und T verwendet werden, müssen folgenden Bedingungen genügen (dabei ist T der Typ, t ein Wert von T und u ein const-Wert von T):

  Kopierkonstruktoren -  T(t) und T(u)

  Destruktor          -  t.~T()

  Adresse von         -  &t und &u, was T* beziehungsweise

                         const T* ergibt

  Zuweisung           -  t = a; dabei ist a ein

                         (eventuell const-) Wert von T

Der Typ, der für den Template-Parameter Compare verwendet wird, muß den Bedingungen für binäre Funktionen genügen.

Beschränkungen

Elementfunktions-Templates werden in allen Containern verwendet, die in der Standardbibliothek zur Verfügung stehen. Ein Beispiel für dieses Merkmal gibt der Konstruktor für multimap<Key,Compare>, der zwei Iterator-Templates annimmt:

template <class InputIterator>

 multimap (InputIterator, InputIterator, Compare);

multimap verfügt darüber hinaus über eine insert-Funktion dieses Typs. Diese Funktionen erlauben den Gebrauch jedes beliebigen Eingabeiterators als Argument, sofern der Compiler dies zuläßt. Für Compiler, die dieses Merkmal nicht unterstützen, stehen Ersatzfunktionen zur Verfügung. Sie erlauben die Verwendung eines Iterators, der aus dem gleichen Container-Typ wie der zu konstruierende (oder derjenige, für den eine Elementfunktion aufgerufen wird) stammt; alternativ kann ein Zeiger auf den im Container enthaltenen Elementtyp eingesetzt werden.

Wenn beispielsweise der verwendete Compiler Elementfunktions-Templates nicht unterstützt, kann eine Multimap auf eine der folgenden beiden Arten konstruiert werden:

multimap<int,int,less<int> > ::value_type intarray[10];

multimap<int,int,less<int> >

   first_multimap(intarray,intarray + 10);

multimap<int, less<int> >

   second_multimap(first_multimap.begin(),first_multimap.end());

nicht jedoch auf diese Weise:

multimap<long, long, less<long> >

long_multimap(first_multimap.begin(),first_multimap.end());

da long_multimap und first_multimap nicht der gleiche Typ sind.

Außerdem unterstützen viele Compiler voreingestellte Template-Argumente nicht. Wenn dies auf Ihren Compiler zutrifft, müssen Sie immer das Template-Argument Compare angeben.

Beispiel

 #include <string>

 #include <map>

 using namespace std;

 typedef multimap<int, string, less<int> > months_type;

 // Ein pair ausgeben

 template <class First, class Second>

 ostream& operator<<(ostream& out,

                     const pair<First,Second>& p)

 {

   cout << p.second << " has " << p.first << " days";

   return out;

 }

 // Eine multimap ausgeben

 ostream& operator<<(ostream& out, months_type l)

 {

   copy(l.begin(),l.end(), ostream_iterator

                <months_type::value_type>(cout,"\n"));

   return out;

 }

 int main(void)

 {

   // eine multimap aus Monaten und der Anzahl Tage

   // der Monate erzeugen

   months_type months;

   typedef months_type::value_type value_type;

   // Monate in die multimap einsetzen

   months.insert(value_type(31, string("January")));

   months.insert(value_type(28, string("Febuary")));

   months.insert(value_type(31, string("March")));

   months.insert(value_type(30, string("April")));

   months.insert(value_type(31, string("May")));

   months.insert(value_type(30, string("June")));

   months.insert(value_type(31, string("July")));

   months.insert(value_type(31, string("August")));

   months.insert(value_type(30, string("September")));

   months.insert(value_type(31, string("October")));

   months.insert(value_type(30, string("November")));

   months.insert(value_type(31, string("December")));

   // Monate ausgeben

   cout << "All months of the year" << endl << months << endl;

   // Monate mit 30 Tagen suchen

   pair<months_type::iterator,months_type::iterator> p =

          months.equal_range(30);

   // Monate mit 30 Tagen ausgeben

   cout << endl << "Months with 30 days" << endl;

   copy(p.first,p.second,

        ostream_iterator<months_type::value_type>(cout,"\n"));

   return 0;

 }

Konstruktoren und Destruktoren

explicit multimap (const Compare& comp = Compare());

Standardkonstruktor. Konstruiert eine leere Multimap, die die optionale Relation comp zum Sortieren der Indizes verwendet, wenn sie angegeben wird.

template <class InputIterator>

multimap (InputIterator first,

          InputIterator last,

          const Compare& comp = Compare());

Konstruiert eine Multimap, die Werte im Bereich [first, last) enthält. Die Erzeugung der neuen Multimap ist nur dann mit Sicherheit erfolgreich, wenn die Iteratoren first und last Werte vom Typ pair<class Key, class Value> zurückgeben.

multimap (const multimap<Key, T, Compare>& x);

Kopierkonstruktor. Erzeugt eine neue Multimap und kopiert dazu alle key- und value-Paare aus x.

~multimap ();

Destruktor. Gibt den gesamten für diese Multimap zugewiesenen Speicher frei.

Zuweisungsoperator

multimap<Key, T, Compare>&

operator= (const multimap<Key, T, Compare>& x);

Zuweisungsoperator. Ersetzt den Inhalt von *this durch eine Kopie der Multimap x.

Iteratoren

iterator begin() ;

Liefert einen bidirektionalen Iterator zurück, der auf das erste in der Multimap gespeicherte Element zeigt. Das "erste" wird durch den Vergleichsoperator Compare der Multimap festgelegt.

const_iterator begin() const;

Liefert einen const_iterator zurück, der auf das erste in der Multimap gespeicherte Element zeigt. Das "erste" wird durch den Vergleichsoperator Compare der Multimap festgelegt.

iterator end() ;

Liefert einen iterator zurück, der auf das letzte in der Multimap gespeicherte Element zeigt.

const_iterator end() const;

Liefert einen const_iterator zurück, der auf das letzte in der Multimap gespeicherte Element zeigt.

reverse_iterator rbegin() ;

Liefert einen reverse_iterator zurück, der auf das erste in der Multimap gespeicherte Element zeigt. Das "erste" wird durch den Vergleichsoperator Compare der Multimap festgelegt.

const_reverse_iterator rbegin() const;

Liefert einen const_reverse_iterator zurück, der auf das erste in der Multimap gespeicherte Element zeigt.

reverse_iterator rend() ;

Liefert einen reverse_iterator zurück, der auf das letzte in der Multimap gespeicherte Element zeigt.

const_reverse_iterator rend() const;

Liefert einen const_reverse_iterator zurück, der auf das letzte in der Multimap gespeicherte Element zeigt.

Elementfunktionen

size_type

count (const key_type& x) const;

Liefert die Anzahl der Elemente in der Multimap mit dem Indexwert x zurück.

bool

empty() const;

Gibt true zurück, wenn die Multimap leer ist, andernfalls false.

pair<iterator,iterator>

equal_range (const key_type& x)

Liefert das Paar (lower_bound(x), upper_bound(x)) zurück.

pair<const_iterator,const_iterator>

equal_range (const key_type& x) const;

Liefert das Paar (lower_bound(x), upper_bound(x)) zurück.

void

erase (iterator position);

Entfernt das Element, auf das der Iterator position zeigt, aus der Multimap.

size_type

erase (const key_type& x);

Entfernt alle Elemente mit dem Indexwert x aus der Multimap, wenn solche vorhanden sind. Gibt die Anzahl der entfernten Elemente zurück.

void

erase (iterator first, iterator last);

Vorausgesetzt, die Iteratoren first und last zeigen auf die gleiche Multimap, und last ist von first aus erreichbar, werden alle Elemente im Bereich [first, last) aus der Multimap entfernt.

iterator

find (const key_type& x);

Durchsucht die Multimap nach einem Paar mit dem Indexwert x und liefert einen Iterator auf dieses Paar zurück, wenn es gefunden wird. Wird kein solches Paar gefunden, wird der Wert end() zurückgegeben.

const_iterator

find (const key_type& x) const;

Ebenso wie find oben, jedoch wird ein const_iterator zurückgegeben.

iterator

insert (const value_type& x);

iterator

insert (iterator position, const value_type& x);

x wird in die Multimap eingefügt. Es kann eine Position angegeben werden, an der die Einfügung vorgenommen werden soll. Wenn die Einfügung direkt hinter position erfolgen kann, geschieht dies in konstanter Ausführungszeit. Andernfalls benötigt die Einfügung die Zeit O(log N).

template <class InputIterator>

void

insert (InputIterator first, InputIterator last);

Kopien von jedem Element im Bereich [first, last) werden in die Multimap eingefügt. Die Iteratoren first und last müssen Werte vom Typ pair<T1,T2> zurückgeben. Diese Operation benötigt ungefähr die Ausführungszeit O(N*log(size()+N)).

key_compare

key_comp() const;

Liefert ein Funktionsobjekt zurück, das Indexwerte mittels der Vergleichsoperation Compare der aktuellen Multimap vergleichen kann.

iterator

lower_bound (const key_type& x)

Liefert einen Iterator auf das kleinste Element in der Multimap zurück, dessen Indexwert key größer oder gleich x ist. Wenn kein solches Element existiert, wird end() zurückgegeben.

const_iterator

lower_bound (const key_type& x)

Ebenso wie lower_bound darüber, liefert jedoch einen const_iterator zurück.

size_type

max_size() const;

Liefert die mögliche Maximalgröße der Multimap zurück.

size_type

size() const;

Liefert die Anzahl Elemente in der Multimap zurück.

void

swap (multimap<Key, T, Compare>& x);

Vertauscht den Inhalt der Multimap x mit dem der aktuellen Multimap *this.

upper_bound (const key_type& x)

Liefert einen Iterator auf das größte Multimap-Element zurück, dessen Indexwert kleiner oder gleich x ist. Existiert kein solches Element, wird end() zurückgegeben.

const_iterator

upper_bound (const key_type& x)

Ebenso wie upper_bound oben, es wird aber ein const_iterator zurückgegeben.

value_compare

value_comp() const;

Liefert ein Funktionsobjekt zurück, das value_types (key-,value-Paare) mittels der Vergleichsoperation Compare der aktuellen Multimap vergleichen kann.

Nicht-Element-Operatoren

bool operator==(const multimap<Key, T, Compare>& x,

                const multimap<Key, T, Compare>& y);

Liefert true zurück, wenn alle Elemente in x elementweise mit allen Elementen in y übereinstimmen und verwendet dazu (T::operator==). Gibt andernfalls false zurück.

bool operator< (const multimap<Key, T, Compare>& x,

                const multimap<Key, T, Compare>& y);

Liefert true zurück, wenn x lexikalisch kleiner ist als y. Gibt andernfalls false zurück.


multiset

Container

Ein assoziativer Container, der schnellen Zugriff auf gespeicherte Indexwerte bietet. Indexwerte können mehrfach auftreten.

Syntax

#include <set>

template <class Key, class Compare = less<Key> >

 class multiset {

public:

// Typdefinitionen

   typedef Key key_type;

   typedef Key value_type;

   typedef Compare key_compare;

   typedef Compare value_compare;

   typedef typename reference;

   typedef typename const_reference;

   typedef typename iterator;

   typedef typename const_iterator;

   typedef typename size_type;

   typedef difference_type;

   typedef reverse_iterator<iterator,

                         value_type,

                         reference,

                         difference_type> reverse_iterator;

   typedef reverse_iterator<const_iterator,

                         value_type,

                         const_reference,

                         difference_type>

const_reverse_iterator;

// Konstruieren/Kopieren/Entfernen

   explicit multiset (const Compare& comp = Compare());

   template <class InputIterator>

    multiset (InputIterator first, InputIterator last,

             const Compare& comp = Compare());

   multiset (const multiset<Key, Compare>& x);

   ~multiset ();

   multiset<Key, Compare>&  operator=  (const  multiset<Key,

   Compare>& x);

// Iteratoren

   iterator begin ();

   const_iterator begin () const;

   iterator end ();

   const_iterator end () const;

   reverse_iterator rbegin ();

   const_reverse_iterator rbegin () const;

   reverse_iterator rend ();

   const_reverse_iterator rend () const;

// Kapazität

   bool empty () const;

   size_type size () const;

   size_type max_size () const;

// Modifizierer

   iterator insert (const value_type& x);

   iterator insert (iterator position, const value_type& x);

   template <class InputIterator>

    void insert (InputIterator first, InputIterator last);

   void erase (iterator position);

   size_type erase (const key_type& x);

   void erase (iterator first, iterator last);

   void swap (multiset<Key, Compare>& x);

// Observer

   pkey_compare key_comp () const;

   value_compare value_comp () const;

// Multiset-Operationen

   iterator find (const key_value& x);

   size_type count (const key_type& x) const;

   iterator lower_bound (const key_type& x);

   iterator upper_bound (const key_type& x);

   pair<iterator, iterator> equal_range (const key_type& x);

   };

template <class Key,

          class Compare>

 bool operator==

    (const multiset<Key, Compare>& x,

     const multiset<Key, Compare>& y);

template <class Key,

          class Compare>

 bool operator<

    (const multiset<Key, Compare>& x,

     const multiset<Key, Compare>& y);

Beschreibung

multiset bietet den schnellen Zugriff auf gespeicherte Indexwerte. Die Standardoperation für den Indexvergleich ist der Operator <. Bei Multisets ist die Einfügung mehrfach vorhandener Indizes möglich.

multiset stellt bidirektionale Iteratoren zur Verfügung, die auf einen gespeicherten Index zeigen.

Jeder Typ, der für den Template-Parameter Key verwendet wird, muß den folgenden Bedingungen genügen (dabei ist T der Typ, t ein Wert von T und u ein const-Wert von T):

  Kopierkonstruktoren  T(t) und T(u)

  Destruktor           t.~T()

  Adresse von          &t und &u, was T* beziehungsweise

                       const T* ergibt

  Zuweisung            t = a; dabei ist a ein

                       (eventuell const-) Wert von T

Der Typ, der für den Template-Parameter Compare verwendet wird, muß den Bedingungen für binäre Funktionen genügen.

Beschränkungen

Elementfunktions-Templates werden in allen Containern verwendet, die in der Standardbibliothek zur Verfügung stehen. Ein Beispiel für dieses Merkmal gibt der Konstruktor für multiset<Key,Compare>, der zwei Iterator-Templates annimmt:

template <class InputIterator>

 multiset (InputIterator, InputIterator);

multiset verfügt darüber hinaus über eine insert-Funktion dieses Typs. Diese Funktionen erlauben den Gebrauch jedes beliebigen Eingabeiterators als Argument, sofern der Compiler dies zuläßt. Für Compiler, die dieses Merkmal nicht unterstützen, stehen Ersatzfunktionen zur Verfügung. Sie erlauben die Verwendung eines Iterators, der aus dem gleichen Container-Typ wie der zu konstruierende (oder derjenige, für den eine Elementfunktion aufgerufen wird) stammt; alternativ kann ein Zeiger auf den im Container enthaltenen Elementtyp eingesetzt werden.

Wenn beispielsweise der verwendete Compiler Elementfunktions-Templates nicht unterstützt, kann ein multiset auf eine der folgenden beiden Arten konstruiert werden:

int intarray[10];

multiset<int,less<int> > first_multiset(intarray,intarray  +10);

multiset<int,                  less<int>                   >

second_multiset(first_multiset.begin(),first_multiset.end());

nicht jedoch auf diese Weise:

multiset<long,                 less<long>                  >

long_multiset(first_multiset.begin(),first_multiset.end());

da long_multiset und first_multiset nicht der gleiche Typ sind.

Außerdem unterstützen viele Compiler voreingestellte Template-Argumente nicht. Wenn dies auf Ihren Compiler zutrifft, müssen Sie immer das Template-Argument Compare angeben.

Beispiel

#include <set>

 using namespace std;

 typedef multiset<int,less<int> > set_type;

 ostream& operator<<(ostream& out, const set_type& s)

 {

   copy(s.begin(),s.end(),

        ostream_iterator<set_type::value_type>(cout," "));

   return out;

 }

 int main(void)

 {

   // ein multiset mit Integerwerten erzeugen

   set_type  si;

   int  i;

   for (int j = 0; j < 2; j++)

   {

     for(i = 0; i < 10; ++i) {

       // Werte mit Wunschposition einfügen

       si.insert(si.begin(), i);

     }

   }

   // multiset ausgeben

   cout << si << endl;

   // Ein weiteres Integer-Multiset und ein leeres Multiset erzeugen

   set_type si2, siResult;

   for (i = 0; i < 10; i++)

      si2.insert(i+5);

   cout << si2 << endl;

   // Zwei Mengenalgorithmen ausprobieren

   set_union(si.begin(),si.end(),si2.begin(),si2.end(),

          inserter(siResult,siResult.begin()));

   cout << "Union:" << endl << siResult << endl;

   siResult.erase(siResult.begin(),siResult.end());

   set_intersection(si.begin(),si.end(),

          si2.begin(),si2.end(),

          inserter(siResult,siResult.begin()));

   cout << "Intersection:" << endl << siResult << endl;

   return 0;

 }

Konstruktor und Destruktor

explicit multiset (const Compare& comp = Compare());

Standardkonstruktor. Konstruiert ein leeres Multiset, das die optionale Relation Compare für die Sortierung der Indizes verwendet, wenn sie angegeben wird.

template <class InputIterator>

multiset (InputIterator first,

             InputIterator last,

             const Compare& comp = Compare());

Konstruiert ein Multiset, das Werte in dem Bereich [first, last) enthält.

multiset (const multiset<Key, Compare>& x);

Kopierkonstruktor. Erzeugt ein neues Multiset, indem alle Indexwerte aus x kopiert werden.

~multiset ();

Destruktor. Gibt den gesamten für dieses Multiset zugewiesenen Speicher frei.

Zuweisungsoperator

multiset<Key, Compare>&

operator=  (const  multiset<Key, Compare>& x);

Zuweisungsoperator. Ersetzt den Inhalt von *this durch eine Kopie des Inhalts von x.

Iteratoren

iterator begin()

Liefert einen Iterator zurück, der auf das erste in der Multiset gespeicherte Element zeigt. Das "erste" ist durch den Vergleichsoperator Compare des Multisets definiert.

const_iterator begin()

Liefert einen const_iterator zurück, der auf das erste in dem Multiset gespeicherte Element zeigt.

iterator end()

Liefert einen Iterator zurück, der auf das letzte in dem Multiset gespeicherte Element zeigt, also auf den Nach-Ende-Wert.

const_iterator end()

Liefert einen const_iterator zurück, der auf das letzte in dem Multiset gespeicherte Element zeigt, also auf den Nach-Ende-Wert.

reverse_iterator rbegin()

Liefert einen reverse_iterator zurück, der auf das erste in dem Multiset gespeicherte Element zeigt. Das "erste" ist durch den Vergleichsoperator Compare des Multisets definiert.

const_reverse_iterator rbegin()

Liefert einen const_reverse_iterator zurück, der auf das erste in dem Multiset gespeicherte Element zeigt.

reverse_iterator rend()

Liefert einen reverse_iterator zurück, der auf das letzte in dem Multiset gespeicherte Element zeigt, d.h. auf den Nach-Ende-Wert.

const_reverse_iterator rend()

Liefert einen const_reverse_iterator zurück, der auf das letzte in dem Multiset gespeicherte Element zeigt, also auf den Nach-Ende-Wert.

Elementfunktionen

size_type

count (const key_type& x) const;

Liefert die Anzahl der Elemente in dem Multiset mit dem Indexwert x zurück.

bool

empty () const;

Gibt true zurück, wenn das Multiset leer ist, andernfalls false.

pair<iterator,iterator>

equal_range (const key_type& x)

Liefert das Paar (lower_bound(x), upper_bound(x)) zurück.

void

erase (iterator position);

Entfernt das Element, auf das der Iterator position zeigt, aus dem Multiset.

size_type

erase (const key_type& x);

Entfernt alle Elemente mit dem Indexwert x aus dem Multiset, wenn solche vorhanden sind. Gibt die Anzahl der entfernten Elemente zurück.

void

erase (iterator first, iterator last);

Vorausgesetzt, die Iteratoren first und last zeigen auf das gleiche Multiset, und last ist von first aus erreichbar, werden alle Elemente in dem Bereich [first, last) aus dem Multiset entfernt.

iterator

find (const key_type& x);

Durchsucht das Multiset nach einem Indexwert x und liefert einen Iterator auf diesen Index zurück, wenn er gefunden wird. Wird kein solcher Wert gefunden, wird der Iterator end() zurückgegeben.

iterator

insert (const value_type& x);

iterator

insert (iterator position, const value_type& x);

x wird in das Multiset eingefügt. Es kann eine Position angegeben werden, an der die Einfügung vorgenommen werden soll. Wenn die Einfügung direkt hinter position erfolgen kann, geschieht dies in konstanter Ausführungszeit. Andernfalls benötigt die Einfügung die Zeit O(log N).

template <class InputIterator>

void

insert (InputIterator first, InputIterator last);

Kopien von jedem Element in dem Bereich [first, last) werden in das Multiset eingefügt. Diese Einfügung benötigt ungefähr die Zeit O(N*log(size()+N)).

key_compare

key_comp () const;

Liefert ein Funktionsobjekt zurück, das Indexwerte mittels der Vergleichsoperation Compare des aktuellen Multisets vergleichen kann.

iterator

lower_bound (const key_type& x)

Liefert einen Iterator auf das kleinste Element in dem Multiset zurück, dessen Indexwert key größer oder gleich x ist. Wenn kein solches Element existiert, wird end() zurückgegeben.

size_type

max_size () const;

Liefert die mögliche Maximalgröße des Multisets zurück.

size () const;

Liefert die Anzahl der Elemente in dem Multiset zurück.

void

swap (multiset<Key, Compare >& x);

Vertauscht den Inhalt des Multisets x mit dem aktuellen Multiset *this.

iterator

upper_bound (const key_type& x)

Liefert einen Iterator auf das größte Element im Multiset zurück, dessen Indexwert key kleiner oder gleich x ist. Wenn kein solches Element existiert, wird end() zurückgegeben.

value_compare

value_comp () const;

Liefert ein Funktionsobjekt zurück, das Indexwerte mittels der Vergleichsfunktion Compare des aktuellen Multisets vergleichen kann. Diese Funktion ist mit key_comp für den Container-Typ set identisch.

Nicht-Element-Operatoren

operator==(const multiset<Key, Compare>& x,

const multiset<Key, Compare>& y);

Liefert true zurück, wenn alle Elemente in x elementweise mit allen Elementen in y übereinstimmen und verwendet dazu (T::operator==). Gibt andernfalls false zurück.

operator< (const multiset<Key, Compare>& x,

const multiset<Key, Compare>& y);

Liefert true zurück, wenn x lexikalisch kleiner ist als y. Gibt andernfalls false zurück.


negate

Funktionsobjekt

Unäres Funktionsobjekt, das die Negation seines Arguments zurückgibt.

Syntax

#include <functional>

   template <class T>

   struct negate : unary_function<T, T, T> {

        T operator() (const T& x) const

              { return -x; }

   };

Beschreibung

negate ist ein unäres Funktionsobjekt. Sein operator() liefert die Negation seines Arguments zurück, das heißt also true, wenn das Argument false ist, oder false, wenn das Argument true ist. Ein Objekt des Typs negate kann an jeden Algorithmus übergeben werden, der eine unäre Funktion annimmt. Beispielsweise wendet der Algorithmus transform eine unäre Operation auf die Werte in einer Kollektion an und speichert das Ergebnis. negate könnte in diesem Algorithmus in der folgenden Weise verwendet werden:

vector<int> vec1;

vector<int> vecResult;

.

.

.

transform(vec1.begin(), vec1.end(),
          vecResult.begin(), negate<int>());

Nach diesem Aufruf von transform enthält vecResult(n) die Negation des Elements in vec1(n).


Negatoren

Funktionsobjekt

Funktionsadapter und Funktionsobjekte, die verwendet werden, um den Sinn von Prädikatfunktionsobjekten umzukehren.

Syntax

#include <functional>

template <class Predicate>

class unary_negate

  : public unary_function<Predicate::argument_type, bool> {

public:

   explicit unary_negate (const Predicate& pred);

   bool operator() (const argument_type& x) const;

};

template<class Predicate>

unary_negate <Predicate> not1 (const Predicate& pred);

template<class Predicate>

class binary_negate

  : public binary_function<Predicate::first_argument_type,

                           Predicate::second_argument_type,

                           bool                            >

{

public:

   explicit binary_negate (const Predicate& pred);

   bool operator() (const first_argument_type& x,

                    const second_argument_type& y) const;

};

template <class Predicate>

binary_negate<Predicate> not2 (const Predicate& pred);

Beschreibung

not1 und not2 sind Funktionen, die Prädikatfunktionsobjekte als Argument annehmen und Prädikatfunktionsobjekte mit dem umgekehrten Sinn zurückgeben. not1 akzeptiert unäre Prädikatfunktionsobjekte und gibt solche zurück. not2 akzeptiert binäre Prädikatfunktionsobjekte und gibt solche zurück. unary_negate und binary_negate sind Funktionsobjektklassen, die Rückgabetypen für die Negatoren not1 und not2 zur Verfügung stellen.

Beispiel

 #include<functional>

 #include<algorithm>

 using namespace std;

 //Erzeugt ein neues Prädikat aus unary_function

 template<class Arg>

 class is_odd : public unary_function<Arg, bool>

 {

   public:

   bool operator()(const Arg& arg1) const

   {

     return (arg1 % 2 ? true : false);

   }

 };

 int main()

 {

   less<int> less_func;

   // not2 auf less anwenden

   cout << (less_func(1,4) ? "TRUE" : "FALSE") << endl;

   cout << (less_func(4,1) ? "TRUE" : "FALSE") << endl;

   cout << (not2(less<int>())(1,4) ? "TRUE" : "FALSE")

        << endl;

   cout << (not2(less<int>())(4,1) ? "TRUE" : "FALSE")

        << endl;

   //Eine Instanz unseres Prädikats erzeugen

   is_odd<int> odd;

   // not1 auf unser benutzerdefiniertes Prädikat anwenden

   cout << (odd(1) ? "TRUE" : "FALSE") << endl;

   cout << (odd(4) ? "TRUE" : "FALSE") << endl;

   cout << (not1(odd)(1) ? "TRUE" : "FALSE") << endl;

   cout << (not1(odd)(4) ? "TRUE" : "FALSE") << endl;

   return 0;

 }


next_permutation

Siehe auch	Algorithmus

Erzeugt aufeinanderfolgende Permutationen einer Sequenz auf der Basis einer ordnenden Funktion.

Syntax

#include <algorithm>

template <class BidirectionalIterator>

 bool next_permutation (BidirectionalIterator first,

                        BidirectionalIterator last);

template <class BidirectionalIterator, class Compare>

 bool next_permutation (BidirectionalIterator first,

                        BidirectionalIterator last, Compare comp);

Beschreibung

Die Algorithmen zur Erzeugung von Permutationen (next_permutation und prev_permutation) gehen davon aus, daß die Menge aller Permutationen der Elemente in einer Sequenz lexikalisch im Sinne der Operatoren < oder comp sortiert ist. Wenn beispielsweise eine Sequenz die Integerwerte 1 2 3 enthält, dann hat sie sechs Permutationen, die, in der Reihenfolge von der ersten zur letzten, so aussehen: 1 2 3 , 1 3 2, 2 1 3, 2 3 1, 3 1 2 und 3 2 1.

Der Algorithmus next_permutation nimmt eine durch den Bereich [first, last) definierte Sequenz an und wandelt sie in ihre nächste Permutation um, wenn das möglich ist. Wenn eine derartige Permutation existiert, führt der Algorithmus die Umwandlung durch und liefert true zurück. Wenn die Permutation nicht existiert, gibt next_permutation false zurück und wandelt die Permutation in ihre "erste" Permutation um (entsprechend der lexikalischen Reihenfolge, die entweder durch den Operator < festgelegt ist - die in der ersten Fassung des Algorithmus verwendete Vorgabe - oder durch comp, die benutzerdefinierte Vergleichsfunktion in der zweiten Fassung des Algorithmus.)

Wenn beispielsweise die durch [first, last) definierte Sequenz die Integer 3 2 1 (in dieser Reihenfolge) enthält, gibt es keine "nächste Permutation". Daher wandelt der Algorithmus die Sequenz in ihre erste Permutation um (1 2 3) und liefert false zurück.

Im Höchstfall werden (last - first)/2 Umwandlungen durchgeführt.

Beispiel

 #include <numeric>    //für accumulate

 #include <vector>        //für vector

 #include <functional> //für kleiner als

 using namespace std;

 int main()

 {

   //Einen Vektor mit einem Integer-Array initialisieren

   int  a1[] = {0,0,0,0,1,0,0,0,0,0};

   char a2[] = "abcdefghji";

   //Erzeugen der Ursprungsmenge und der Kopien für die Umwandlung

   vector<int>  m1(a1, a1+10);

   vector<int>  prev_m1((size_t)10), next_m1((size_t)10);

   vector<char> m2(a2, a2+10);

   vector<char> prev_m2((size_t)10), next_m2((size_t)10);

   copy(m1.begin(), m1.end(), prev_m1.begin());

   copy(m1.begin(), m1.end(), next_m1.begin());

   copy(m2.begin(), m2.end(), prev_m2.begin());

   copy(m2.begin(), m2.end(), next_m2.begin());

   //Erzeugen der Permutationen

   prev_permutation(prev_m1.begin(),

                    prev_m1.end(),less<int>());

   next_permutation(next_m1.begin(),

                    next_m1.end(),less<int>());

   prev_permutation(prev_m2.begin(),

                    prev_m2.end(),less<int>());

   next_permutation(next_m2.begin(),

                    next_m2.end(),less<int>());

   //Ergebnisse ausgeben

   cout << "Example 1: " << endl << "     ";

   cout << "Original values:      ";

   copy(m1.begin(),m1.end(),

        ostream_iterator<int>(cout," "));

   cout << endl << "     ";

   cout << "Previous permutation: ";

   copy(prev_m1.begin(),prev_m1.end(),

        ostream_iterator<int>(cout," "));

   cout << endl<< "     ";

   cout << "Next Permutation:     ";

   copy(next_m1.begin(),next_m1.end(),

        ostream_iterator<int>(cout," "));

   cout << endl << endl;

   cout << "Example 2: " << endl << "     ";

   cout << "Original values: ";

   copy(m2.begin(),m2.end(),

        ostream_iterator<char>(cout," "));

   cout << endl << "     ";

   cout << "Previous Permutation: ";

   copy(prev_m2.begin(),prev_m2.end(),

        ostream_iterator<char>(cout," "));

   cout << endl << "     ";

   cout << "Next Permutation:     ";

   copy(next_m2.begin(),next_m2.end(),

        ostream_iterator<char>(cout," "));

   cout << endl << endl;

   return 0;

 }


not1

Siehe auch	Funktionsadapter

Funktionsadapter, der verwendet wird, um den Sinn eines unären Prädikatfunktionsobjekts umzukehren.

Syntax

#include <functional>

template<class Predicate>

unary_negate <Predicate> not1 (const Predicate& pred);

Beschreibung

not1 ist ein Funktionsadapter, ein sogenannter Negator, der ein unäres Prädikatfunktionsobjekt als Argument annimmt und ein unäres Prädikatfunktionsobjekt zurückliefert, das das Komplement des Originals darstellt. unary_negate ist eine Funktionsobjektklasse, die einen Rückgabetyp für den Negator not1 zur Verfügung stellt.

Beachten Sie, daß not1 nur mit Funktionsobjekten eingesetzt werden kann, die als Unterklassen der Klasse unary_function definiert sind.


not2

Siehe auch	Funktionsadapter

Funktionsadapter, der verwendet wird, um den Sinn eines binären Prädikatfunktionsobjekts umzukehren.

Syntax

#include <functional>

template <class Predicate>

binary_negate<Predicate> not2 (const Predicate& pred);

Beschreibung

not2 ist ein Funktionsadapter, ein sogenannter Negator, der ein binäres Prädikatfunktionsobjekt als Argument annimmt und ein binäres Prädikatfunktionsobjekt zurückliefert, das das Komplement des Originals darstellt. binary_negate ist eine Funktionsobjektklasse, die einen Rückgabetyp für den Negator not2 zur Verfügung stellt.

Beachten Sie, daß not2 nur für Funktionsobjekte verwendet werden kann, die als Unterklassen der Klasse binary_function definiert sind.


not_equal_to

Funktionsobjekt

Binäres Funktionsobjekt, das true zurückgibt, wenn sein erstes Argument nicht mit seinem zweiten übereinstimmt.

Syntax

#include <functional>

    template <class T>

    struct not_equal_to : binary_function<T, T, bool> {

         bool operator() (const T& x, const T& y) const

               { return x != y; }

    };

Beschreibung

not_equal_to ist ein binäres Funktionsobjekt. Sein operator() liefert true zurück, wenn x ungleich y ist. Ein Objekt des Typs not_equal_to kann an jeden Algorithmus übergeben werden, der eine binäre Funktion annimmt. Beispielsweise wendet der Algorithmus transform eine binäre Operation auf korrespondierende Werte in zwei Kollektionen an und speichert das Ergebnis. not_equal_to würde in diesem Algorithmus in der folgenden Weise eingesetzt:

vector<int> vec1;

vector<int> vec2;

vector<int> vecResult;

.

.

.

transform(vec1.begin(), vec1.end(),
          vec2.begin(), vec2.end(),
          vecResult.begin(), not_equal_to<int>());

Nach diesem Aufruf von transform enthält vecResult(n) eine "1", wenn vec1(n) nicht gleich vec2(n) war oder eine "0", wenn vec1(n) gleich vec2(n) war.


nth_element

Algorithmus

Ordnet eine Kollektion neu, so daß alle Elemente, die in der sortierten Reihenfolge kleiner als das nte Element sind, vor ihm stehen, und alle Elemente, die in der sortierten Reihenfolge größer als das nte Element sind, nach ihm stehen.

Syntax

#include <algorithm>

template <class RandomAccessIterator>

 void nth_element (RandomAccessIterator first,

                   RandomAccessIterator nth,

                   RandomAccessIterator last);

template <class RandomAccessIterator, class Compare>

 void nth_element (RandomAccessIterator first,

                   RandomAccessIterator nth,

                   RandomAccessIterator last,

                   Compare comp);

Beschreibung

Der Algorithmus nth_element ordnet eine Kollektion neu nach Maßgabe entweder des Standard-Vergleichsoperators (>) oder des zur Verfügung gestellten Vergleichsoperators. Nach der Anwendung des Algorithmus treffen diese drei Bedingungen zu:

Das Element, das an nter Position stehen würde, wenn die Kollektion vollständig sortiert würde, befindet sich an nter Position.

Alle Elemente, die vor der nten Position stehen, würden in einer geordneten Kollektion ebenfalls vor den nten Position stehen.

Alle Elemente, die auf die nte Position folgen, würden auch in einer geordneten Kollektion auf die nte Position folgen.

Das heißt, für jeden Iterator i in dem Bereich [first, nth) und für jeden Iterator j in dem Bereich [nth, last) trifft zu daß !(*i > *j) oder comp(*i, *j) == false.

Beachten Sie, daß die Elemente, die der nten Position vorhergehen oder auf diese folgen, nicht notwendig auch relativ zueinander sortiert sind. Der Algorithmus nth_element sortiert nicht die gesamte Kollektion.

Der Algorithmus ist im Durchschnitt linear; dabei ist N die Größe des Bereichs [first,last).

Beispiel

 #include<algorithm>

 #include<vector>

 using namespace std;

 template<class RandomAccessIterator>

 void quik_sort(RandomAccessIterator start,

                RandomAccessIterator end)

 {

   size_t dist = 0;

   distance(start, end, dist);

   //Stop-Bedingung für Rekursion

   if(dist > 2)

   {

     //nth_element verwenden, um die Arbeit von quik_sort zu übernehmen

     nth_element(start, start+(dist/2), end);

     //Rekursive Aufrufe an alle verbleibenden unsortierten Teile

     quik_sort(start, start+(dist/2-1));

     quik_sort(start+(dist/2+1), end);

   }

   if(dist == 2 && *end < *start)

     swap(start, end);

 }

 int main()

 {

   //Initialisieren eines Vektors mit einem Integer-Array

   int arr[10] = {37, 12, 2, -5, 14, 1, 0, -1, 14, 32};

   vector<int> v(arr, arr+10);

   //Ursprünglichen Vektor ausgeben

   cout << "The unsorted values are: " << endl << "     ";

   vector<int>::iterator i;

   for(i = v.begin(); i != v.end(); i++)

     cout << *i << ", ";

   cout << endl << endl;

   //Neuen Sortieralgorithmus verwenden

   quik_sort(v.begin(), v.end());

   //Sortierten Vektor ausgeben

   cout << "The sorted values are: " << endl << "     ";

   for(i = v.begin(); i != v.end(); i++)

     cout << *i << ", ";

   cout << endl << endl;

   return 0;

 }


numeric_limits

Bibliothek numerischer Grenzen

Eine Klasse für die Darstellung von Informationen über skalare Typen.

Spezialisierte Implementation

numeric_limits<float>

numeric_limits<double>

numeric_limits<long double>

numeric_limits<short>

numeric_limits<unsigned short>

numeric_limits<int>

numeric_limits<unsigned int>

numeric_limits<long>

numeric_limits<unsigned long>

numeric_limits<char>

numeric_limits<wchar_t>

numeric_limits<unsigned char>

numeric_limits<signed char>

numeric_limits<bool>

Syntax

#include <limits>

template <class T>

 class numeric_limits {

 public:

 // Allgemein -- für alle Spezialisierungen von Bedeutung.

    static const bool is_specialized ;

    static T min ();

    static T max ();

    static const int radix ;

    static const int digits ;

    static const int digits10 ;

    static const bool is_signed ;

    static const bool is_integer ;

    static const bool is_exact ;

    static const bool traps ;

    static const bool is_modulo ;

    static const bool is_bounded ;

 // Speziell für Gleitkomma.

    static T epsilon ();

    static T round_error ();

    static const int min_exponent10 ;

    static const int max_exponent10 ;

    static const int min_exponent ;

    static const int max_exponent ;

    static const bool has_infinity ;

    static const bool has_quiet_NaN ;

    static const bool has_signaling_NaN ;

    static const bool is_iec559 ;

    static const bool has_denorm ;

    static const bool tinyness_before ;

    static const float_round_style round_style ;

    static T denorm_min ();

    static T infinity ();

    static T quiet_NaN ();

    static T signaling_NaN ();

 };

 enum float_round_style {

   round_indeterminate       = -1,

   round_toward_zero         =  0,

   round_to_nearest          =  1,

   round_toward_infinity     =  2,

   round_toward_neg_infinity =  3

 };

Beschreibung

numeric_limits ist eine Klasse für die Darstellung von Informationen über skalare Typen. Für jeden grundlegenden Typ stehen Spezialisierungen zur Verfügung, sowohl für Integer- als auch für Gleitkommatypen, einschließlich bool.

Diese Klasse kapselt Informationen, die sich in den Headern <climits> und <cfloat> finden. Darüber hinaus stellt sie zusätzliche Informationen zur Verfügung, die nicht in aktuell existierenden C- oder C++ Headern enthalten sind.

Nicht alle der von den Elementen zur Verfügung gestellten Informationen sind für alle Spezialisierungen von numeric_limits von Bedeutung. Jeder Wert, der für einen bestimmten Typ keine Bedeutung hat, wird auf 0 oder auf false gesetzt.

Warnung:	Die Spezialisierungen für wide chars und bool stehen nur auf Systemen zur Verfügung, bei denen diese Typen real implementiert sind, nicht auf solchen, die sie nur mit Typdefinitionen simulieren.

Beispiel

 #include <limits>

 using namespace std;

 int main()

 {

    numeric_limits<float> float_info;

    if (float_info.is_specialized && float_info.has_infinity)

    {

      // Wert von infinity ermitteln

      float finfinity=float_info.infinity();

    }

    return 0;

 }

Elementfelder und Funktionen

static T

denorm_min ();

Liefert den kleinsten denormalisierten Wert zurück. Für alle Gleitkommatypen von Bedeutung. Bei Typen, die keine denormalisierten Werte zulassen, muß diese Methode den kleinsten normalisierten Wert zurückliefern.

static const int

digits ;

Anzahl der Basisstellen, die ohne Überlauf dargestellt werden können. Bei originären Integertypen ist digits normalerweise die Anzahl der Bits der Darstellung exklusive Vorzeichen. Bei Gleitkommatypen ist digits die Anzahl der Basisstellen in der Mantisse. Dieses Element ist für alle Spezialisierungen von Bedeutung, die is_bounded mit true deklarieren.

static const int

digits10 ;

Anzahl der Dezimalstellen, die ohne Überlauf dargestellt werden können. Für alle Spezialisierungen von Bedeutung, die is_bounded als true deklarieren.

static T

epsilon ();

Liefert den Epsilonwert des Rechners zurück (die Differenz zwischen 1 und dem kleinsten Wert größer als 1, der dargestellt werden kann). Diese Funktion ist nur bei Gleitkommatypen von Bedeutung.

static const bool

has_denorm ;

Dieses Feld ist dann true, wenn der Typ denormalisierte Werte zuläßt (variable Bitanzahl für die Exponentendarstellung). Dieser Wert hat nur für Gleitkommatypen Bedeutung.

static const bool

has_infinity ;

Dieses Feld ist dann true, wenn der Typ eine Darstellung für positiv Unendlich umfaßt. Der Wert hat nur bei Gleitkommatypen eine Bedeutung. Dieses Feld muß bei allen Typen true sein, die IEC 559-Konformität beanspruchen.

static const bool

has_quiet_NaN ;

Dieses Feld ist true, wenn der Typ eine Darstellung für ein stummes (nicht signalisiertes) "Not a Number" umfaßt. Dies ist nur für Gleitkommatypen von Bedeutung und muß für alle Typen true sein, die IEC 559-Konformität beanspruchen.

static const bool

has_signaling_NaN ;

Dieses Feld ist true, wenn der Typ eine Darstellung für ein signalisiertes "Not a Number" umfaßt. Dies ist nur bei Gleitkommatypen von Bedeutung und muß für alle Typen true sein, die IEC 559-Konformität beanspruchen.

static T

infinity ();

Liefert die Darstellung für positiv Unendlich zurück, wenn eine solche vorhanden ist. Diese Elementfunktion hat nur bei solchen Spezialisierungen eine Bedeutung, die has_infinity als true deklarieren. Für alle Typen erforderlich, die IEC 559-Konformität beanspruchen.

static const bool

is_bounded ;

Dieses Feld ist true, wenn die von diesem Typ darstellbare Wertmenge endlich ist. Alle originären C-Typen sind beschränkt; das Element wäre false bei Typen mit beliebiger Genauigkeit.

static const bool

is_exact ;

Dieses statische Elementfeld ist true, wenn der Typ eine exakte Darstellung verwendet. Alle Integertypen sind exakt, die Umkehrung gilt jedoch nicht. Beispielsweise sind rationale Darstellungen und solche mit fixem Exponenten exakt aber keine Integer. Dieses Element ist für alle spezialisierten Implementationen von Bedeutung.

static const bool

is_iec559 ;

Dieses Element ist true, wenn der Typ der IEC-Norm 559 entspricht, und nur dann. Es hat nur für Gleitkommatypen eine Bedeutung. Es muß für alle Typen, die IEC 559-Konformität beanspruchen, true sein.

static const bool

is_integer ;

Dieses Element ist true, wenn der Typ ein Integertyp ist. Es hat für alle Spezialisierungen Bedeutung.

static const bool

is_modulo ;

Dieses Feld ist true, wenn der Typ modulo ist. Dieser Typ ist grundsätzlich false für Gleitkommatypen, true für vorzeichenlose Integer und ebenso true für Integer mit Vorzeichen (dies gilt jedoch nur auf den meisten Systemen). Ein Typ ist dann modulo, wenn es möglich ist, zwei positive Zahlen zu addieren und ein Ergebnis zu erhalten, das einen Überlauf in eine dritte Zahl ergibt, die ihrerseits kleiner als die Ausgangswerte ist.

static const bool

is_signed ;

Dieses Element ist true, wenn der Typ ein Vorzeichen trägt. Dieses Element ist für alle Spezialisierungen von Bedeutung.

static const bool

is_specialized ;

Zeigt an, ob numeric_limits für den Typ T spezialisiert wurde. Dieses Feld muß für alle Spezialisierungen von numeric_limits true sein. In der Standard-Template numeric_limits<T> muß dieses Flag dagegen false sein.

static T

max ();

Liefert den größten finiten Wert zurück. Diese Funktion hat bei allen Spezialisierungen eine Bedeutung, die is_bounded als true deklarieren.

static const int

max_exponent ;

Größter positiver Integerwert, und zwar in dem Sinn, daß die Basis mit diesem Wert potenziert sich innerhalb des Bereichs befindet. Dieses Feld hat nur bei Gleitkommatypen eine Bedeutung.

static const int

max_exponent10 ;

Größter positiver Integerwert in dem Sinn, daß 10 mit diesem Wert potenziert sich innerhalb des Bereichs befindet. Dieses Feld ist nur für Gleitkommatypen von Bedeutung.

static T

min ();

Liefert den kleinsten finiten Wert zurück. Bei Gleitkommatypen mit denormalisierter Darstellung muß min()den kleinsten normalisierten Wert zurückliefern. Der kleinste denormalisierte Wert wird durch denorm_min() zurückgegeben. Diese Funktion ist für alle Spezialisierungen von Bedeutung, die is_bounded als true deklarieren.

static const int

min_exponent ;

Kleinster negativer Integerwert in dem Sinn, daß die Basis mit diesem Wert potenziert innerhalb des Bereichs liegt. Dieses Feld hat nur für Gleitkommatypen Bedeutung.

static const int

min_exponent10 ;

Kleinster negativer Integerwert in dem Sinn, daß 10 mit diesem Wert potenziert innerhalb des Bereichs liegt. Dieses Feld hat nur für Gleitkommatypen Bedeutung.

static T

quiet_NaN ();

Liefert die Darstellung eines stummen (nicht signalisierten) "Not a Number" zurück, wenn diese verfügbar ist. Diese Funktion hat nur bei solchen Spezialisierungen eine Bedeutung, die has_quiet_NaN als true deklarieren. Dieses Feld ist für jeden Typ erforderlich, der IEC 559-Konformität beansprucht.

static const int

radix ;

Gibt bei Gleitkommatypen die Basis (Radix) der Exponentendarstellung an (oftmals 2). Bei Integertypen muß dieses Element die Basis der Darstellung angeben. Dieses Feld ist für alle Spezialisierungen von Bedeutung.

static T

round_error ();

Liefert das Ausmaß des maximalen Rundungsfehlers zurück. Diese Funktion ist nur für Gleitkommatypen von Bedeutung.

static const float_round_style

round_style ;

Der Rundungsstil für den Typ. Spezialisierungen für Integertypen müssen round_toward_zero zurückliefern. Dieses Feld ist für alle Gleitkommatypen von Bedeutung.

static T

signaling_NaN();

Liefert die Darstellung eine signalisierenden "Not a Number" zurück, wenn diese verfügbar ist. Diese Funktion hat nur für solche Spezialisierungen Bedeutung, die has_signaling_NaN als true deklarieren. Diese Funktion muß für jeden Typ eine Bedeutung haben, der IEC 559-Konformität beansprucht.

static const bool

tinyness_before ;

Dieses Element ist true, wenn vor dem Runden eine geringe Abweichung festgestellt wird. Es ist nur bei Gleitkommatypen von Bedeutung.

static const bool

traps ;

Dieses Feld ist dann true, wenn für diesen Typ die Wertverfolgung (trapping) implementiert ist. Das Feld traps hat für alle Spezialisierungen Bedeutung.


operator!=, operator>, operator<=, operator>=

Hilfs-Operatoren

Operatoren für die C++ Standardbibliothek.

Syntax

#include <utility>

template <class T>

 bool operator!= (const T& x, const T& y)

template <class T>

 bool operator> (const T& x, const T& y)

template <class T>

 bool operator<= (const T& x, const T& y)

template <class T>

 bool operator>= (const T& x, const T& y)

Beschreibung

Um redundante Definitionen von operator != aus operator == sowie der Operatoren >, <= und >= aus dem Operator < zu vermeiden, stehen in der Standardbibliothek diese Definitionen zur Verfügung:

    operator != returns !(x==y),

    operator >  returns y<x,

    operator <= returns !(y<x), and

    operator >= returns !(x<y).


ostream_iterator

Siehe auch	Iterator

Stream-Iteratoren stellen Iteratorenmerkmale für Ausgabe-Streams (ostreams) und Eingabe-Streams (istreams) zur Verfügung. Mit ihrer Hilfe ist es möglich, generische Algorithmen direkt auf Streams anzuwenden.

Syntax

#include <iterator>

template <class T>

 class ostream_iterator : public output_iterator<T, Distance>

{

 public:

  ostream_iterator(ostream& s);

  ostream_iterator (ostream& s, const char* delimiter);

  ostream_iterator (const ostream_iterator<T>& x);

  ~ostream_itertor ();

  ostream_iterator<T>& operator=(const  T&  value);

  ostream_iterator<T>& operator* () const;

  ostream_iterator<T>& operator++ ();

  ostream_iterator<T> operator++ (int);

 };

template <class T, class Distance> inline bool

operator== (const istream_iterator<T, Distance>& x,
      const istream_iterator<T, Distance>& y);

Beschreibung

Stream-Iteratoren stellen die Standard-Iteratorenschnittstelle für Eingabe- und Ausgabe-Streams dar.

Die Klasse ostream_iterator schreibt Elemente in einen Ausgabe-Stream. Bei Verwendung des zweiten Konstruktors, der über ein zweites Argument char * verfügt, wird der in diesem Argument angegebene String nach jedem Element geschrieben. (Der String muß nullterminiert sein.) Da ein ostream-Iterator ein Ausgabe-Iterator ist, besteht keine Möglichkeit, ein Element aus dem Iterator auszulesen. Einem solchen Iterator können lediglich Elemente zugewiesen werden.

Konstruktoren

ostream_iterator (ostream& s);

Konstruiert einen ostream_iterator auf den angegebenen Stream.

ostream_iterator (ostream& s, const char* delimiter);

Konstruiert einen ostream_iterator auf den angegebenen Stream. Der nullterminierte String-Begrenzer wird nach jedem Element in den Stream geschrieben.

ostream_iterator (const ostream_iterator<T>& x);

Kopierkonstruktor.

Destruktor

~ostream_iterator ();

Destruktor

Operatoren

const T& operator= (const T& value);

Verschiebt den Wert T in den Ausgabe-Stream.

const T& ostream_iterator<T>&

operator* ();

ostream_iterator<T>&

operator++();

ostream_iterator<T>

operator++ (int);

Alle diese Operatoren führen keine Aktionen aus. Sie dienen lediglich dazu, die Verwendung des Iterators in den gängigen Konstrukten zu ermöglichen.

Beispiel

 #include <iterator>

 #include <numeric>

 #include <deque>

 using namespace std;

 int main ()

 {

  //

  // Initialisieren eines Vektors unter Verwendung eines Arrays.

  //

  int arr[4] = { 3,4,7,8 };

  int total=0;

  deque<int> d(arr+0, arr+4);

  //

  // den gesamten Vektor und eine Summe nach cout streamen

  //

  copy(d.begin(),d.end()-1,ostream_iterator<int>(cout," + "));

  cout << *(d.end()-1) << " = " <<

     accumulate(d.begin(),d.end(),total) << endl;

  return 0;

 }


Ausgabeiterator

Siehe auch	Iterator

Ein vorwärtsschreitender Iterator für reinen Schreibzugriff.

Beschreibung

Hinweis:	Eine vollständige Beschreibung der Iteratoren finden Sie im Abschnitt Iteratoren dieses Referenzhandbuchs.

Iteratoren sind eine Verallgemeinerung von Zeigern, die es einem C++ Programm erlauben, auf konsistente Weise mit unterschiedlichen Datenstrukturen zusammenzuarbeiten. Ausgabeiteratoren sind Iteratoren für reinen Schreibzugriff, die vorwärts schreiten und den unten aufgeführten Bedingungen genügen. Beachten Sie, daß Ausgabeiteratoren, im Gegensatz zu anderen Iteratoren, die in der Standardbibliothek verwendet werden, nicht konstant sein können.

In den unten aufgeführten Iteratorbeschreibungen werden die folgenden Bezeichner verwendet:

a und b	Werte vom Typ X

n	Wert vom Typ distance

u, Distance, tmp und m	Bezeichner.

r	Wert vom Typ X&

t	Wert vom Typ T

Anforderungen an Ausgabeiteratoren

Die folgenden Ausdrücke müssen für Ausgabeiteratoren gültig sein:

X(a)	Kopierkonstruktor, a == X(a).

X u(a)	Kopierkonstruktor, u == a

X u = a	Zuweisung, u == a

*a = t	Ergebnis wird nicht verwendet.

++r	liefert X& zurück.

r++	Rückgabewert kann in const X& umgewandelt werden.

*r++ = t	Ergebnis wird nicht verwendet.

Der Operator * kann nur auf der linken Seite einer Zuweisung verwendet werden.

Algorithmen, die Ausgabeiteratoren verwenden, sollten Single-Pass-Algorithmen sein. D.h. der gleiche Iterator sollte nicht zweimal durchlaufen werden.


pair

Hilfsklasse

Ein Template für ein heterogenes Wertepaar.

Syntax

#include <utility>

template <class T1, class T2>

 struct pair {

        T1 first;

        T2 second;

        pair (const T1& x, const T2& y);

};

template <class T1, class T2>

 bool operator== (const pair<T1, T2>& x,

                  const pair T1, T2>& y);

template <class T1, class T2>

 bool operator< (const pair<T1, T2>& x,

                 const pair T1, T2>& y);

template <class T1, class T2>

 pair<T1,T2> make_pair (const T1&, const T2&);

Beschreibung

Die Klasse pair stellt ein Template für die Kapselung eines Wertepaars zur Verfügung; die Werte können verschiedenen Typs sein.

Konstruktor

pair (const T1& x, const T2& y);

Der Konstruktor erzeugt ein Paar mit den Typen T1 und T2 und nimmt die erforderlichen Umwandlungen in x und y vor.

Operatoren

template <class T1, class T2>

 bool operator== (const pair<T1, T2>& x,

                  const pair T1, T2>& y);

Liefert true zurück, wenn (x.first == y.first && x.second == y.second) true ist. Gibt andernfalls false zurück.

template <class T1, class T2>

 bool operator< (const pair<T1, T2>& x,

                 const pair T1, T2>& y);

Liefert true zurück, wenn (x.first < y.first || (!(y.first < x.first) && x.second < y.second)) true ist. Gibt andernfalls false zurück.

Elementfunktionen

template <class T1, class T2>

pair<T1,T2>

make_pair(x,y)

make_pair(x,y) erzeugt ein Paar, indem es die Typen von x und y ableitet und zurückliefert.


partial_sort

Siehe auch	Algorithmus

Algorithmen-Template für das Sortieren von Kollektionen.

Syntax

#include <algorithm>

template <class RandomAccessIterator>

 void partial_sort (RandomAccessIterator first,

                    RandomAccessIterator middle,

                    RandomAccessIterator last);

template <class RandomAccessIterator, class Compare>

 void partial_sort (RandomAccessIterator first,

                    RandomAccessIterator middle,

                    RandomAccessIterator last, Compare comp);

Beschreibung

Der Algorithmus partial_sort nimmt den Bereich [first,last) an und positioniert die ersten middle-first-Werte in Sortierreihenfolge. Im Ergebnis ist dann der Bereich [first,middle) genau so sortiert, wie er es auch bei der Sortierung des gesamten Bereichs ([first,last)) wäre. Die verbleibenden Elemente im Bereich (die in [middle, last)) stehen nicht in einer irgendwie definierten Reihenfolge. Die erste Fassung des Algorithmus verwendet kleiner als (<) als Vergleichsoperator für die Sortierung. Die zweite Fassung verwendet die Vergleichsfunktion comp.

partial_sort führt näherungsweise (last - first) * log(middle-first) Vergleiche durch.


partial_sort_copy

Siehe auch	Algorithmus

Algorithmen-Template für das Sortieren von Kollektionen.

Syntax

#include <algorithm>

template <class InputIterator,

          class RandomAccessIterator>

 void partial_sort_copy (InputIterator first,

                         InputIterator last,

                         RandomAccessIterator result_first,

                         RandomAccessIterator result_last);

template <class InputIterator,

          class RandomAccessIterator,

          class Compare>

 void partial_sort_copy (InputIterator first,

                         InputIterator last,

                         RandomAccessIterator result_first,

                         RandomAccessIterator result_last,

                         Compare comp);

Beschreibung

Der Algorithmus partial_sort_copy setzt den kleineren der Bereiche sortierter Elemente last-first und result_last - result_first aus dem Bereich [first,last) in den Bereich ein, der bei result_first beginnt. (Das heißt also den Bereich: [result_first, result_first+min(last-first, result_last- result_first)). Im Prinzip ergibt sich so das gleiche Resultat, als ob der Bereich [first,last) in einen temporären Puffer geschrieben und dort sortiert, und anschließend so viele Elemente wie möglich in den Bereich [result_first,result_last) kopiert würden.

Die erste Fassung des Algorithmus verwendet kleiner als (<) als Vergleichsoperator für die Sortierung. Die zweite Fassung verwendet die Vergleichsfunktion comp.

partial_sort_copy führt näherungsweise (last-first) * log(min(last-first, result_last-result_first)) Vergleiche durch.

Beispiel

/**************************************************************

 *

 * partsort.cpp - Beispielprogramm für partielle Sortierung.

 *

 * $Id: partsort.cpp,v 1.6 1995/10/06 19:03:56 hart Exp $

 *

 * $$RW_INSERT_HEADER "slyrs.str"

 *

 ***************************************************************/

 #include <vector>

 #include <algorithm>

 using namespace std;

 int main()

 {

  int d1[20] = {17, 3, 5, -4, 1, 12, -10, -1, 14, 7,

         -6, 8, 15, -11, 2, -2, 18, 4, -3, 0};

  //

  // Einen Vektor einrichten.

  //

  vector<int> v1(d1+0, d1+20);

  //

  // Ursprünglichen Vektor ausgeben.

  //

  cout << "For the vector: ";

  copy(v1.begin(), v1.end(), ostream_iterator<int>(cout," "));

  //

  // Partielles Sortieren der ersten sieben Elemente.

  //

  partial_sort(v1.begin(), v1.begin()+7, v1.end());

  //

  // Ergebnis ausgeben.

  //

  cout << endl << endl << "A partial_sort of 7 elements gives: "

    << endl << "   ";

  copy(v1.begin(), v1.end(), ostream_iterator<int>(cout," "));

  cout << endl;

  //

  // Ein Vektor mit zehn Elementen.

  //

  vector<int> v2(10, 0);

  //

  // Die letzten zehn Elemente in v1 in v2 hinein sortieren.

  //

  partial_sort_copy(v1.begin()+10, v1.end(), v2.begin(),
           v2.end());

  //

  // Ergebnis ausgeben.

  //

  cout << endl << "A partial_sort_copy of the last ten elements
          gives: " << endl << "   ";

  copy(v2.begin(), v2.end(), ostream_iterator<int>(cout," "));

  cout << endl;

  return 0;

 }


partial_sum

Generalisierte numerische Operation

Berechnet aufeinanderfolgende Teilsummen in einem Wertebereich.

Syntax

#include <numeric>

template <class InputIterator, class OutputIterator>

OutputIterator partial_sum (InputIterator first,

                            InputIterator last,

                            OutputIterator result);

template <class InputIterator,

          class OutputIterator,

          class BinaryOperation>

OutputIterator partial_sum (InputIterator first,

                            InputIterator last,

                            OutputIterator result,

                            BinaryOperation binary_op);

Beschreibung

Der Algorithmus partial_sum erzeugt eine neue Sequenz, in der jedes Element durch Addition aller Werte der vorangegangenen Elemente, oder in der zweiten Fassung des Algorithmus durch Anwenden der Operation binary_op auf jedes der vorangegangenen Elemente nacheinander gebildet wird. Das heißt, partial_sum weist jedem Iterator i in dem Bereich [result, result + (last - first)) einen Wert zu, der gleich:

((...(*first + *(first + 1)) + ... ) + *(first + (i - result)))

ist. In der zweiten Fassung des Algorithmus ist er gleich:

binary_op(binary_op(..., binary_op (*first, *(first + 1)),...),*(first + (i - result)))

Beispielsweise ergibt die Anwendung von partial_sum auf (1,2,3,4,) die Ausgabe (1,3,6,10).

Der Algorithmus partial_sum liefert result + (last - first) zurück.

Wenn result gleich first ist, ersetzen die Elemente der neuen Sequenz sukzessiv die Elemente in der Originalsequenz; dann verhält sich partial_sum im Ergebnis wie eine stationäre Transformation.

Es werden exakt (last - first) - 1 Anwendungen des Standardoperators + oder der Operation binary_op ausgeführt.

Beispiel

 #include <numeric>   //für accumulate

 #include <vector>    //für vector

 #include <functional> //für times

 using namespace std;

 int main()

 {

   //Initialisieren eines Vektors mittels eines Integer-Arrays

   int d1[10] = {1,2,3,4,5,6,7,8,9,10};

   vector<int> v(d1, d1+10);

   //Erzeugen eines leeren Vektors f zum Speichern der Ergebnisse

   vector<int> sums((size_t)10), prods((size_t)10);

   //Berechnen der partial_sums und partial_products

   partial_sum(v.begin(), v.end(), sums.begin());

   partial_sum(v.begin(), v.end(), prods.begin(), times<int>());

   //Ausgeben der Ergebnisse

   cout << "For the series: " << endl << "     ";

   copy(v.begin(),v.end(),ostream_iterator<int>(cout," "));

   cout << endl << endl;

   cout << "The partial sums: " << endl << "     " ;

   copy(sums.begin(),sums.end(),

        ostream_iterator<int>(cout," "));

   cout <<" should each equal (N*N + N)/2" << endl << endl;

   cout << "The partial products: " << endl << "     ";

   copy(prods.begin(),prods.end(),

        ostream_iterator<int>(cout," "));

   cout << " should each equal N!" << endl;

   return 0;

 }


partition

Siehe auch	Algorithmus

Plaziert alle Elemente, die dem angegebenen Prädikat genügen, vor allen Elementen, auf die das Prädikat nicht zutrifft.

Syntax

#include <algorithm>

template <class BidirectionalIterator, class Predicate>

BidirectionalIterator

partition (BidirectionalIterator first,

           BidirectionalIterator last,

           Predicate pred);

Beschreibung

Der Algorithmus partition plaziert alle Elemente in dem Bereich [first, last), die dem Prädikat pred genügen, vor allen Elementen, die pred nicht genügen. Er liefert einen Iterator zurück, der auf das Element nach dem Ende der Gruppe der Elemente zeigt, die pred genügen. Anders ausgedrückt: partition gibt ein i zurück, so daß für jeden Iterator j in dem Bereich [first, i), pred(*j) == true, und für jeden Iterator k in dem Bereich [i, last), pred(*j) == false gilt.

Beachten Sie, daß partition nicht unbedingt die relative Ordnung der Elemente, die dem Prädikat genügen, sowie der Elemente, die dem Prädikat nicht genügen, beibehält. Verwenden Sie den Algorithmus stable_partition, wenn die Einhaltung der relativen Ordnung entscheidend ist.

Der Algorithmus partition führt höchstens (last - first)/2 Tauschvorgänge aus und wendet das Prädikat exakt last - first mal an.

Beispiel

/**************************************************************

 *

 * prtition.cpp - Beispielprogramm für partition.

 *

 * $Id: prtition.cpp,v 1.7 1995/10/06 19:18:57 hart Exp $

 *

 * $$RW_INSERT_HEADER "slyrs.str"

 *

 **************************************************************/

 #include<functional>

 #include<deque>

 #include<algorithm>

 using namespace std;

 //

 // Erzeugt ein neues Prädikat aus unary_function.

 //

 template<class Arg>

 class is_even : public unary_function<Arg, bool>

 {

  public:

  bool operator()(const Arg& arg1) { return (arg1 % 2) == 0; }

 };

 int main ()

 {

  //

  // Initialisiert einen Deque mit einem Integer-Array.

  //

  int init[10] = { 1,2,3,4,5,6,7,8,9,10 };

  deque<int> d1(init+0, init+10);

  deque<int> d2(init+0, init+10);

  //

  // Gibt die Ursprungswerte aus.

  //

  cout << "Unpartitioned values: " << "\t\t";

  copy(d1.begin(), d1.end(), ostream_iterator<int>(cout," "));

  cout << endl;

  //

  // Eine Aufteilung des Deques nach dem Kriterium gerade/ungerade.

  //

  partition(d2.begin(), d2.end(), is_even<int>());

  //

  // Ergebnis der Aufteilung ausgeben.

  //

  cout << "Partitioned values: " << "\t\t";

  copy(d2.begin(), d2.end(), ostream_iterator<int>(cout," "));

  cout << endl;

  //

  // Eine stabile Aufteilung des Deques nach dem Kriterium gerade/ungerade.

  //

  stable_partition(d1.begin(), d1.end(), is_even<int>());

  //

  // Ergebnis der Aufteilung ausgeben.

  //

  cout << "Stable partitioned values: " << "\t";

  copy(d1.begin(), d1.end(), ostream_iterator<int>(cout," "));

  cout << endl;

  return 0;

 }


permutation

Siehe auch	Algorithmus

Erzeugt aufeinanderfolgende Permutationen einer Sequenz auf der Grundlage einer Ordnungsfunktion.


plus

Funktionsobjekt

Ein binäres Funktionsobjekt, das das Ergebnis der Addition seines ersten und seines zweiten Arguments zurückliefert.

Syntax

#include <functional>

   template<class T>

   struct plus : binary_function<T, T, T> {

        T operator() (const T& x, const T& y) const

              { return x + y; }

   };

Beschreibung

plus ist ein binäres Funktionsobjekt. Sein operator() liefert das Ergebnis der Addition von x und y zurück. Ein Objekt des Typs plus kann an jeden Algorithmus übergeben werden, der eine binäre Funktion annimmt. Beispielsweise wendet der Algorithmus transform eine binäre Operation auf die korrespondierenden Werte in zwei Kollektionen an und speichert das Ergebnis. plus würde in diesem Algorithmus in der folgenden Weise verwendet:

vector<int> vec1;

vector<int> vec2;

vector<int> vecResult;

.

.

.

transform(vec1.begin(), vec1.end(),
          vec2.begin(), vec2.end(),
          vecResult.begin(), plus<int>());

Nach diesem Aufruf von transform enthält vecResult(n) vec1(n) plus vec2(n).


pointer_to_binary_function

Ein Funktionsobjekt, das einen Zeiger auf eine binäre Funktion adaptiert, damit der Zeiger an Stellen eingesetzt werden kann, die eine binary_function verlangen.

Syntax

#include <functional>

template <class Arg1, class Arg2, class Result>

 class pointer_to_binary_function : public binary_function<Arg1, Arg2, Result> {

 public:

    explicit pointer_to_binary_function (Result (*f)(Arg1, Arg2));

    Result operator() (const Arg1& x, const Arg2& y) const;

};

template<class Arg1, class Arg2, class Result>

pointer_to_binary_function<Arg1, Arg2, Result>

  ptr_fun (Result (*x)(Arg1, Arg2));

Beschreibung

Die Klasse pointer_to_binary_function kapselt einen Zeiger als Funktion mit zwei Argumenten. Die Klasse stellt einen operator() zur Verfügung, so daß das resultierende Objekt als binäres Funktionsobjekt für diese Funktion dient.

Die Funktion ptr_fun ist überladen, um Instanzen einer pointer_to_binary_function zu erzeugen, wenn ihr der entsprechende Zeiger auf eine Funktion zur Verfügung gestellt wird.


pointer_to_unary_function

Siehe auch	Funktionsobjekt

Ein Funktionsobjekt, das einen Zeiger auf eine Funktion adaptiert, damit der Zeiger an Stellen eingesetzt werden kann, die eine unary_function erfordern.

Syntax

#include <functional>

template <class Arg, class Result>

 class pointer_to_unary_function : public unary_function<Arg, Result> {

 public:

    explicit pointer_to_unary_function (Result (*f)(Arg));

    Result operator() (const Arg& x) const;

};

template<class Arg, class Result>

pointer_to_unary_function<Arg, Result>

  ptr_fun (Result (*f)(Arg));

Beschreibung

Die Klasse pointer_to_unary_function kapselt einen Zeiger in einer Funktion mit einem Argument. Die Klasse stellt einen operator() zur Verfügung, so daß das resultierende Objekt als ein Funktionsobjekt für diese Funktion dient.

Die Funktion ptr_fun ist überladen, um Instanzen von pointer_to_unary_function zu erzeugen, wenn ihr der entsprechende Zeiger auf eine Funktion zur Verfügung gestellt wird.


pop_heap

Siehe auch	Algorithmus

Verschiebt das größte Element vom Heap.

Syntax

template <class RandomAccessIterator>

  void

  pop_heap(RandomAccessIterator first,

           RandomAccessIterator last);

template <class RandomAccessIterator, class Compare>

  void

  pop_heap(RandomAccessIterator first,

           RandomAccessIterator last, Compare comp);

Beschreibung

Ein Heap ist eine besondere Organisationsform von Elementen in einem Bereich zwischen zwei Random Access-Iteratoren [a, b). Die zwei Hauptmerkmale sind:

1.	*a ist das größte Element innerhalb des Bereichs.

2.	*a kann durch den Algorithmus pop_heap entfernt werden, oder es kann ein neues Element durch den Algorithmus push_heap hinzugefügt werden. Jeder dieser Vorgänge erfolgt in der Zeit O(logN).

Diese Eigenschaften machen Heaps als Ereigniswarteschlangen nutzbar.

Der Algorithmus pop_heap verwendet den Operator kleiner als (<) als Standard-Vergleichsoperator. Ein alternativer Vergleichsoperator kann angegeben werden.

Der Algorithmus pop_heap kann im Rahmen einer Operation zum Entfernen des größten Elements von einem Heap verwendet werden. Er geht davon aus, daß der Bereich [first, last) ein gültiger Heap ist (das heißt, daß first das größte Element in dem Heap oder das erste Element auf der Grundlage des alternativen Vergleichsoperators ist). Er vertauscht dann den Wert an der Position first mit dem Wert an der Position last - 1 und ordnet dann [first, last -1)wieder als Heap. Auf das Element in last kann über die Elementfunktion back() der Klassen vector oder deque zugegriffen werden, oder das Element kann mit der Elementfunktion pop_back entfernt werden. Beachten Sie, daß pop_heap das Element nicht wirklich aus der Datenstruktur entfernt; dafür muß zusätzlich eine andere Funktion verwendet werden.

pop_heap führt höchstens 2 * log(last - first) Vergleiche aus.

Beispiel

 #include <algorithm>

 #include <vector>

 using namespace std;

 int main(void)

 {

   int d1[4] = {1,2,3,4};

   int d2[4] = {1,3,2,4};

   // Zwei Vektoren einrichten

   vector<int> v1(d1,d1 + 4), v2(d2,d2 + 4);

   // Heaps erzeugen

   make_heap(v1.begin(),v1.end());

   make_heap(v2.begin(),v2.end(),less<int>());

   // v1 = (4,x,y,z)  und  v2 = (4,x,y,z)

   // Beachten Sie, daß x, y und z die verbleibenden Werte

   // in dem Container (also mit Ausnahme von 4) darstellen.

   // Die Definition des Heaps und der Heap-Operationen

   // erfordern keine besondere Anordnung dieser

   // Werte.

   // Beide Vektoren nach cout kopieren

   ostream_iterator<int> out(cout," ");

   copy(v1.begin(),v1.end(),out);

   cout << endl;

   copy(v2.begin(),v2.end(),out);

   cout << endl;

   // Jetzt verschieben (pop)

   pop_heap(v1.begin(),v1.end());

   pop_heap(v2.begin(),v2.end(),less<int>());

   // v1 = (3,x,y,4) and v2 = (3,x,y,4)

   // Beide Vektoren nach cout kopieren

   copy(v1.begin(),v1.end(),out);

   cout << endl;

   copy(v2.begin(),v2.end(),out);

   cout << endl;

   // Und nun hinzufügen (push)

   push_heap(v1.begin(),v1.end());

   push_heap(v2.begin(),v2.end(),less<int>());

   // v1 = (4,x,y,z) and v2 = (4,x,y,z)

   // Beide Vektoren nach cout kopieren

   copy(v1.begin(),v1.end(),out);

   cout << endl;

   copy(v2.begin(),v2.end(),out);

   cout << endl;

   // Nun die Heaps sortieren

   sort_heap(v1.begin(),v1.end());

   sort_heap(v2.begin(),v2.end(),less<int>());

   // v1 = v2 = (1,2,3,4)

   // Beide Vektoren nach cout kopieren

   copy(v1.begin(),v1.end(),out);

   cout << endl;

   copy(v2.begin(),v2.end(),out);

   cout << endl;

   return 0;

 }


Prädikat

Eine Funktion oder ein Funktionsobjekt, die/das einen boolschen Wert (true/false) oder einen Integerwert zurückgibt.


prev_permutation

Siehe auch	Algorithmus

Erzeugt aufeinanderfolgende Permutationen einer Sequenz auf der Grundlage einer Ordnungsfunktion.

Syntax

#include <algorithm>

template <class BidirectionalIterator>

 bool prev_permutation (BidirectionalIterator first,

                        BidirectionalIterator last);

template <class BidirectionalIterator, class Compare>

 bool prev_permutation (BidirectionalIterator first,

                        BidirectionalIterator last, Compare comp);

Beschreibung

Die Algorithmen zum Erzeugen von Permutationen (next_permutation und prev_permutation) gehen davon aus, daß die Menge aller Permutationen der Elemente in einer Sequenz lexikalisch im Sinne der Operatoren < oder comp sortiert ist. Wenn eine Sequenz beispielsweise die Integer 1 2 3 enthält, dann hat sie sechs Permutationen, die - in der Reihenfolge von der ersten zur letzten - lauten: 1 2 3 , 1 3 2, 2 1 3, 2 3 1, 3 1 2 und 3 2 1.

Der Algorithmus prev_permutation nimmt eine Sequenz an, die durch den Bereich [first, last) definiert ist, und wandelt sie in ihre vorhergehende Permutation um, wenn das möglich ist. Wenn eine solche Permutation existiert, führt der Algorithmus die Transformation durch und liefert true zurück. Wenn die Permutation nicht existiert, gibt prev_permutation false zurück und transformiert die Permutation zu ihrer "letzten" Permutation (im Sinne der lexikalischen Ordnung, die entweder durch den Operator < - den Standardoperator, der in der ersten Fassung des Algorithmus verwendet wird - definiert ist, oder durch comp, die vom Benutzer zur Verfügung gestellte Vergleichsfunktion, die in der zweiten Fassung des Algorithmus zum Einsatz kommt.)

Wenn beispielsweise die durch [first, last) definierte Sequenz die Integer 1 2 3 (in dieser Reihenfolge) enthält, dann gibt es keine "vorhergehende Permutation". Aus diesem Grund wandelt der Algorithmus die Sequenz in ihre letzte Permutation um (3 2 1) und gibt false zurück.

Es werden höchstens (last - first)/2 Vertauschungen ausgeführt.

Beispiel

 #include <numeric>    //für accumulate

 #include <vector>        //für vector

 #include <functional> //für kleiner als (less)

 using namespace std;

 int main()

 {

   //Initialisieren eines Vektors unter Verwendung eines Integer-Arrays

   int  a1[] = {0,0,0,0,1,0,0,0,0,0};

   char a2[] = "abcdefghji";

   //Erzeugen der Anfangsmenge und der Kopien für die Permutation

   vector<int>  m1(a1, a1+10);

   vector<int>  prev_m1((size_t)10), next_m1((size_t)10);

   vector<char> m2(a2, a2+10);

   vector<char> prev_m2((size_t)10), next_m2((size_t)10);

   copy(m1.begin(), m1.end(), prev_m1.begin());

   copy(m1.begin(), m1.end(), next_m1.begin());

   copy(m2.begin(), m2.end(), prev_m2.begin());

   copy(m2.begin(), m2.end(), next_m2.begin());

   //Erzeugen der Permutationen

   prev_permutation(prev_m1.begin(),

                    prev_m1.end(),less<int>());

   next_permutation(next_m1.begin(),

                    next_m1.end(),less<int>());

   prev_permutation(prev_m2.begin(),

                    prev_m2.end(),less<int>());

   next_permutation(next_m2.begin(),

                    next_m2.end(),less<int>());

   //Ergebnisse ausgeben

   cout << "Example 1: " << endl << "     ";

   cout << "Original values:      ";

   copy(m1.begin(),m1.end(),

        ostream_iterator<int>(cout," "));

   cout << endl << "     ";

   cout << "Previous permutation: ";

   copy(prev_m1.begin(),prev_m1.end(),

        ostream_iterator<int>(cout," "));

   cout << endl<< "     ";

   cout << "Next Permutation:     ";

   copy(next_m1.begin(),next_m1.end(),

        ostream_iterator<int>(cout," "));

   cout << endl << endl;

   cout << "Example 2: " << endl << "     ";

   cout << "Original values: ";

   copy(m2.begin(),m2.end(),

        ostream_iterator<char>(cout," "));

   cout << endl << "     ";

   cout << "Previous Permutation: ";

   copy(prev_m2.begin(),prev_m2.end(),

        ostream_iterator<char>(cout," "));

   cout << endl << "     ";

   cout << "Next Permutation:     ";

   copy(next_m2.begin(),next_m2.end(),

        ostream_iterator<char>(cout," "));

   cout << endl << endl;

   return 0;

 }


priority_queue

Container

Ein Container-Adapter, der sich wie eine Warteschlange verhält. Elemente, die aus der Queue entfernt werden, sind im Hinblick auf eine "Priorität" geordnet.

Syntax

#include <queue>

template <class T,

          class Container = vector<T>,

          class Compare = less<Container::value_type>>

 class priority_queue {

public:

// Typdefinitionen

   typedef typename Container::value_type value_type;

   typedef typename Container::size_type size_type;

protected:

   Container c;

   Compare comp;

public:

   explicit priority_queue (const Compare& x = Compare());

   template <class InputIterator>

    priority_queue (InputIterator first,

                    InputIterator last,

                    const Compare& x = Compare());

   bool empty () const;

   size_type size () const;

   const value_type& top () const;

   void push (const value_type& x)

   void pop()

};

Beschreibung

priority_queue ist ein Container-Adapter, der es einem Container gestattet, sich wie eine Ereigniswarteschlange zu verhalten. Das bedeutet, daß das Element mit der höchsten Priorität, wie sie entweder durch den Standard-Vergleichsoperator (operator <) oder durch die Vergleichsfunktion comp definiert ist, an den Anfang der Warteschlange gestellt wird, wann immer Elemente zu der Warteschlange hinzugefügt oder aus ihr entfernt werden.

priority_queue kann jeden Container adaptieren, der front(), push_back() und pop_back() zur Verfügung stellt.

Beschränkungen

Wenn Ihr Compiler keine Standard-Template-Parameter unterstützt, müssen Sie immer einen Container-Template-Parameter zur Verfügung stellen, wenn Sie eine Instanz von priority_queue deklarieren. Sie könnten beispielsweise nicht schreiben

priority_queue<int> var;

Statt dessen müßten Sie schreiben

priority_queue<int, vector<int>> var;

Beispiel

 #include <queue>

 #include <deque>

 #include <vector>

 #include <string>

 using namespace std;

 int main(void)

 {

   // Eine Warteschlange mit Werten vom Typ int anlegen und dazu einen
 // Deque-Container verwenden

   priority_queue<int, vector<int>, less<int> > pq;

   // Zwei Werte einfügen

   pq.push(1);

   pq.push(2);

   // Zwei Werte entfernen (Pop) und die Enden untersuchen

   cout << pq.top() << endl;

   pq.pop();

   cout << pq.top() << endl;

   pq.pop();

   // Eine Ereigniswarteschlange von Strings anlegen

   priority_queue<string,deque<string>, less<string> > pqs;

   // Einige Strings in die Warteschlange einfügen und sie anschließend wieder
 // entfernen

   int i;

   for (i = 0; i < 10; i++)

   {

     pqs.push(string(i+1,'a'));

     cout << pqs.top() << endl;

   }

   for (i = 0; i < 10; i++)

   {

     cout << pqs.top() << endl;

     pqs.pop();

   }

   // Eine Ereigniswarteschlange von Strings anlegen und dazu größer verwenden

   priority_queue<string,deque<string>, greater<string> > pgqs;

   // Einige Strings einfügen und sie anschließend wieder entfernen

   for (i = 0; i < 10; i++)

   {

     pgqs.push(string(i+1,'a'));

     cout << pgqs.top() << endl;

   }

   for (i = 0; i < 10; i++)

   {

     cout << pgqs.top() << endl;

     pgqs.pop();

   }

   return 0;

 }

Konstruktor

explicit priority_queue (const Compare& x = Compare());

Standardkonstruktor. Konstruiert eine Ereigniswartschlange, die Container für ihre zugrundeliegende Implementation und Compare als Standardfunktion für die Bestimmung der Priorität verwendet.

template <class InputIterator>

    priority_queue (InputIterator first, InputIterator last,

                    const Compare& x = Compare());

Konstruiert eine neue Ereigniswartschlange und plaziert jedes Element im Bereich [first, last) darin.

Elementfunktionen

bool

empty () const;

Gibt true zurück, wenn die priority_queue leer ist, andernfalls false.

void

pop();

Entfernt das Element mit der höchsten Priorität aus der Warteschlange.

void

push (const value_type& x);

Fügt der Warteschlange das Element x hinzu.

size_type

size () const;

Liefert die Anzahl der Elemente in der priority_queue zurück.

const value_type&

top () const;

Liefert eine Referenz auf das Element mit der höchsten Priorität in der Warteschlange zurück.


ptr_fun

Siehe auch	Funktionsadapter

Eine Funktion, die überladen ist, um einen Zeiger auf eine Funktion zu adaptieren, damit der Zeiger an Stellen eingesetzt werden kann, die eine Funktion erfordern.

Syntax

#include <functional>

template<class Arg, class Result>

pointer_to_unary_function<Arg, Result>

  ptr_fun (Result (*f)(Arg));

template<class Arg1, class Arg2, class Result>

pointer_to_binary_function<Arg1, Arg2, Result>

  ptr_fun (Result (*x)(Arg1, Arg2));

Beschreibung

Die Klassen pointer_to_unary_function und pointer_to_binary_function kapseln Zeiger auf Funktionen und stellen einen operator() zur Verfügung, so daß das resultierende Objekt als Funktionsobjekt für die Funktion dient.

Die Funktion ptr_fun ist überladen, um Instanzen von pointer_to_unary_function oder pointer_to_binary_function zu erzeugen, wenn ihr der entsprechende Zeiger auf eine Funktion zur Verfügung gestellt wird.

Beispiel

 #include<functional>

 #include<deque>

 #include<vector>

 #include<algorithm>

 using namespace std;

 //Erzeugen einer Funktion

 int factorial(int x)

 {

   int result = 1;

   for(int i = 2; i <= x; i++)

 result *= i;

   return result;

 }

 int main()

 {

   //Initialisieren eines Deques mit einem Integer-Array

   int init[7] = {1,2,3,4,5,6,7};

   deque<int> d(init, init+7);

   //Erzeugen eines leeren Vektors zum Speichern der Faktoren

   vector<int> v((size_t)7);

   //Umwandeln der Zahlen in dem Deque in ihre Faktoren

   //und Speichern im Vektor

   transform(d.begin(), d.end(), v.begin(), ptr_fun(factorial));

   //Ausgeben der Ergebnisse

   cout << "The following numbers: " << endl << "     ";

   copy(d.begin(),d.end(),ostream_iterator<int>(cout," "));

   cout << endl << endl;

   cout << "Have the factorials: " << endl << "     ";

   copy(v.begin(),v.end(),ostream_iterator<int>(cout," "));

   return 0;

 }


push_heap

Siehe auch	Algorithmus

Plaziert ein neues Element auf einem Heap.

Syntax

#include <algorithm>

template <class RandomAccessIterator>

  void

  push_heap(RandomAccessIterator first,

            RandomAccessIterator last);

template <class RandomAccessIterator, class Compare>

  void

  push_heap(RandomAccessIterator first,

            RandomAccessIterator last, Compare comp);

Beschreibung

Ein Heap ist eine bestimmte Organisationsform von Elementen in einem Bereich zwischen zwei Random Access-Iteratoren [a, b). Seine beiden Hauptmerkmale sind:

1.	*a ist das größte Element in dem Bereich.

2.	*a kann durch den Algorithmus pop_heap entfernt werden, oder ein neues Element kann mit dem Algorithmus push_heap hinzugefügt werden; dies erfordert die Zeit O(logN).

Diese Eigenschaften machen Heaps für die Verwendung als Ereigniswarteschlangen nützlich.

Der Algorithmus push_heap verwendet den Operator kleiner als (<) als Standard-Vergleichsoperator. Wie bei allen Algorithmen zur Bearbeitung von Heaps kann eine alternative Vergleichsfunktion festgelegt werden.

Der Algorithmus push_heap wird verwendet, um dem Heap ein neues Element hinzuzufügen. Zuerst wird ein neues Element für den Heap am Ende eines Bereichs eingefügt. (Beispielsweise können Sie die Elementfunktion push_back()der Klassen vector oder deque verwenden, um das Element am Ende eines dieser beiden Container einzufügen.) Der Algorithmus push_heap geht davon aus, daß der Bereich [first, last - 1) ein gültiger Heap ist. Er plaziert dann das Element an der Position last - 1 an seiner korrekten Position auf dem Heap. Es ergibt sich so ein Heap im Bereich [first, last).

Beachten Sie, daß der Algorithmus push_heap nicht seinerseits ein Element in den dem Heap zugrundeliegenden Container einfügt. Das Element muß zunächst mit einer anderen Funktion am Ende des Containers hinzugefügt werden, ehe push_heap angewendet werden kann.

Für push_heap werden höchsten log(last - first) Vergleiche ausgeführt.

Beispiel

 #include <algorithm>

 #include <vector>

 using namespace std;

 int main(void)

 {

   int d1[4] = {1,2,3,4};

   int d2[4] = {1,3,2,4};

   // Zwei Vektoren einrichten

   vector<int> v1(d1,d1 + 4), v2(d2,d2 + 4);

   // Heaps anlegen

   make_heap(v1.begin(),v1.end());

   make_heap(v2.begin(),v2.end(),less<int>());

   // v1 = (4,x,y,z)  und  v2 = (4,x,y,z)

   // Beachten Sie, daß x, y und z die verbleibenden

   // Werte in dem Container (also ungleich 4) darstellen.

   // Die Definition des Heaps und der Heap-Operationen

   // setzt keine bestimmte Anordnung dieser Werte

   // voraus.

   // Beide Vektoren nach cout kopieren

   ostream_iterator<int> out(cout," ");

   copy(v1.begin(),v1.end(),out);

   cout << endl;

   copy(v2.begin(),v2.end(),out);

   cout << endl;

   // Nun Element entfernen (pop)

   pop_heap(v1.begin(),v1.end());

   pop_heap(v2.begin(),v2.end(),less<int>());

   // v1 = (3,x,y,4) and v2 = (3,x,y,4)

   // Beide Vektoren nach cout kopieren

   copy(v1.begin(),v1.end(),out);

   cout << endl;

   copy(v2.begin(),v2.end(),out);

   cout << endl;

   // Und nun hinzufügen (push)

   push_heap(v1.begin(),v1.end());

   push_heap(v2.begin(),v2.end(),less<int>());

   // v1 = (4,x,y,z) and v2 = (4,x,y,z)

   // Beide Vektoren nach cout kopieren

   copy(v1.begin(),v1.end(),out);

   cout << endl;

   copy(v2.begin(),v2.end(),out);

   cout << endl;

   // Jetzt diese Heaps sortieren

   sort_heap(v1.begin(),v1.end());

   sort_heap(v2.begin(),v2.end(),less<int>());

   // v1 = v2 = (1,2,3,4)

// Beide Vektoren nach cout kopieren

   copy(v1.begin(),v1.end(),out);

   cout << endl;

   copy(v2.begin(),v2.end(),out);

   cout << endl;

   return 0;

 }


queue

Siehe auch	Container

Ein Container-Adapter, der sich wie eine Warteschlange verhält (first in, first out).

Syntax

#include <queue>

template <class T, class Container = deque<T>>

 class queue {

public:

// Typdefinitionen

   typedef typename Container::value_type value_type;

   typedef typename Container::size_type size_type;

protected:

   Container c;

public:

// Elementzugriff

   bool empty () const { return c.empty; }

   size_type size () const { return c.size; }

   value_type& front () { return c.front; }

   const value_type& front () const { return c.front(); }

   value_type& back () { return c.back; }

   const value_type& back () const { return c.back(); }

   void push (const value_type& x) { c.push_back(x); }

   void pop () { c.pop_back(); }

};

template <class T, class Container>

 bool operator== (const queue<T, Container>& x,

                  const queue<T Container>& y)

   { return x.c == y.c; }

template <class T, class Container>

 bool operator< (const queue<T, Container>& x,

                  const queue<T, Container>& y)

   { return x.c < y.c; }

Beschreibung

Der Container-Adapter queue läßt einen Container wie eine Warteschlange agieren. In einer Warteschlange werden Elemente am Ende des Containers eingefügt und am Anfang entfernt. Die Elemente, die einer Warteschlange zuerst hinzugefügt werden, sind die Elemente, die auch als erste wieder entfernt werden (first in, first out, auch "FIFO" genannt).

queue kann für die Adaptierung jedes Containers verwendet werden, der die Operationen front(), back(), push_back() und pop_front() unterstützt. Insbesondere können list und deque verwendet werden.

Beschränkungen

Wenn der verwendete Compiler keine voreingestellten Template-Parameter unterstützt, muß immer ein Container-Template-Parameter angegeben werden. Beispielsweise kann dann diese Schreibweise nicht verwendet werden:

queue<int> var;

statt dessen müßte dies so aussehen:

queue<int, deque<int>> var;

Beispiel

 #include <queue>

 #include <string>

 #include <deque>

 #include <list>

 using namespace std;

 int main(void)

 {

   // Anlegen einer Warteschlange unter Verwendung eines Deque-Containers

   queue<int, list<int> > q;

   // Zwei Werte einsetzen und sie anschließend entfernen

   q.push(1);

   q.push(2);

   cout << q.front() << endl;

   q.pop();

   cout << q.front() << endl;

   q.pop();

   // Eine Warteschlange von Strings anlegen und dazu einen Deque-Container
 // verwenden

   queue<string,deque<string> > qs;

   // Einige Strings einfügen und sie anschließend wieder entfernen

   int i;

   for (i = 0; i < 10; i++)

   {

     qs.push(string(i+1,'a'));

     cout << qs.front() << endl;

   }

   for (i = 0; i < 10; i++)

   {

     cout << qs.front() << endl;

     qs.pop();

   }

   return 0;

 }

Elementfunktionen

value_type&

back ();

Liefert das Element am Ende der Warteschlange zurück (das zuletzt in die Warteschlange eingefügte Element).

const value_type&

back() const;

Liefert das Element am Ende der Warteschlange als const value_type zurück.

bool

empty () const;

Liefert true zurück, wenn die Warteschlange leer ist, andernfalls false.

value_type&

front ();

Liefert das Element am Anfang der Warteschlange zurück. Dies ist das erste in die Warteschlange eingefügte Element, es sei denn, daß pop() seitdem aufgerufen wurde.

const value_type&

front () const;

Liefert das Element am Anfang der Warteschlange als const value_type zurück.

void

pop ();

Entfernt das Element am Anfang der Warteschlange.

void

push (const value_type& x);

Fügt x am Ende der Warteschlange ein.

size_type

size () const;

Liefert die Anzahl der Elemente in der Warteschlange zurück.


Random Access-Iterator

Siehe auch	Iterator

Ein Iterator, der Lese- und Schreibzugriff im direkten Zugriff auf einen Container zur Verfügung stellt.

Beschreibung

Hinweis:	Eine vollständige Beschreibung von Iteratoren finden Sie unter Iteratoren in diesem Referenzteil.

Iteratoren stellen eine Verallgemeinerung von Zeigern dar, die es einem C++ Programm erlauben, in einheitlicher Weise mit verschiedenen Datenstrukturen zu arbeiten. Random Access-Iteratoren können für Lese- und Schreibzugriff verwendet werden, und sie bieten direkten Zugriff auf die Container, die sie bedienen. Diese Iteratoren genügen den unten aufgeführten Bedingungen.

Die folgenden Bezeichner werden in der Auflistung der Anforderungen an Iteratoren verwendet:

a und b	Werte vom Typ X

n	Wert vom Typ distance

u, Distance, tmp und m	Bezeichner

r	Wert vom Typ X&

t	Wert vom Typ T

Anforderungen an Random Access-Iteratoren

Die folgenden Ausdrücke müssen für Random Access-Iteratoren gültig sein:

X u	u kann einen einzelnen Wert haben.
X()	X() kann ein Einzelwert sein.
X(a)	Kopierkonstruktor, a == X(a).
X u(a)	Kopierkonstruktor, u == a
X u = a	Zuweisung, u == a
a == b, a != b	Rückgabewert, der in bool umgewandelt werden kann.
*a	Rückgabewert, der in T& umgewandelt werden kann.
++r	Liefert X& zurück.
r++	Rückgabewert, der in const X& umgewandelt werden kann.
*r++	liefert T& zurück.
--r	liefert X& zurück.
r--	Rückgabewert, der in const X& umgewandelt werden kann.
*r--	liefert T& zurück.
r += n	Semantik von --r oder ++r n hängt vom Vorzeichen von n ab.
a + n, n + a	liefert den Typ X zurück.
r -= n	liefert X& zurück, verhält sich wie r += -n
a - n	liefert Typ X zurück.
b - a	liefert Distance zurück.
a[n]	*(a+n), Rückgabewert kann in T umgewandelt werden.
a < b	Über-Alles-Ordnungsrelation.
a > b	umgekehrte Über-Alles-Ordnungsrelation zu <
a <= b	!(a < b)
a >= b	!(a > b)
Wie für Vorwärtsiteratoren muß auch für Random Access-Iteratoren gelten, daß a == b immer *a== *b impliziert.

Die Zahl der Durchgänge, die ein Algorithmus in der Struktur durchführen kann, ist nicht beschränkt.

Alle relationalen Operatoren liefern einen Wert zurück, der in bool umgewandelt werden kann.


random_shuffle

Algorithmus

Ordnet Elemente einer Kollektion zufällig an.

Syntax

#include <algorithm>

template <class RandomAccessIterator>

 void random_shuffle (RandomAccessIterator first,

                      RandomAccessIterator last);

template <class RandomAccessIterator,

          class RandomNumberGenerator>

 void random_shuffle (RandomAccessIterator first,

                      RandomAccessIterator last,

                      RandomNumberGenerator& rand);

Beschreibung

Der Algorithmus random_shuffle mischt die Elemente im Bereich [first, last) mit einheitlicher Verteilung durcheinander. random_shuffle kann ein bestimmtes Funktionsobjekt zur Erzeugung von Zufallszahlen rand annehmen; dabei nimmt rand ein positives Argument n mit der Distanz type des RandomAccessIterator und liefert einen zufällig ermittelten Wert zwischen 0 und n - 1 zurück.

Der Algorithmus random_shuffle führt (last - first) -1 Vertauschungen aus.

Beispiel

 #include<algorithm>

 #include<vector>

 using namespace std;

 int main()

 {

   //Initialisieren eines Vektors mit einem Integer-Array

   int arr[10] = {1,2,3,4,5,6,7,8,9,10};

   vector<int> v(arr, arr+10);

   //Elemente in der ursprünglichen (sortierten) Reihenfolge ausgeben

   cout << "Elements before random_shuffle: " << endl << "     ";

   copy(v.begin(),v.end(),ostream_iterator<int>(cout," "));

   cout << endl << endl;

   //Elemente mit random_shuffle durcheinandermischen

   random_shuffle(v.begin(), v.end());

   //Die durcheinandergemischten Elemente ausgeben

   cout << "Elements after random_shuffle: " << endl << "     ";

   copy(v.begin(),v.end(),ostream_iterator<int>(cout," "));

   cout << endl;

   return 0;

 }


raw_storage_iterator

Speicherverwaltung

Erlaubt es Iterator-basierten Algorithmen, Ergebnisse in nicht initialisiertem Arbeitsspeicher abzulegen.

Syntax

#include <memory>

template <class OutputIterator, class T>

 class raw_storage_iterator : public output_iterator {

public:

   explicit raw_storage_iterator (OutputIterator x);

   raw_storage_iterator<OutputIterator, t>& operator*();

   raw_storage_iterator<OutputIterator, T>&

     operator= (const T& element);

   raw_storage_iterator<OutputIterator>& operator++();

   raw_storage_iterator<OutputIterator> operator++ (int);

};

Beschreibung

Die Klasse raw_storage_iterator ermöglicht es Iterator-basierten Algorithmen, ihre Ergebnisse in nicht initialisierten Bereichen des Arbeitsspeichers abzulegen. Für den Template-Parameter OutputIterator ist es erforderlich, daß sein operator * ein Objekt zurückliefert, für das der operator & definiert ist und einen Zeiger auf T zurückliefert.

Konstruktor

raw_storage_iterator (OutputIterator x);

Initialisiert den Iterator, so daß er auf den gleichen Wert zeigt, auf den x zeigt.

Elementoperatoren

raw_storage_iterator <OutputIterator, T> &

  operator =(const T& element);

Konstruiert eine Instanz von T, die zu dem Wert element initialisiert ist, an der Position, auf die der Iterator zeigt.

raw_storage_iterator <OutputIterator, T>&

operator++()

Prä-Inkrement: bewegt den Iterator vorwärts und liefert eine Referenz auf den aktualisierten Iterator zurück.

raw_storage_iterator<OutputIterator>

 operator++ (int);

Post-Inkrement: bewegt den Iterator vorwärts und liefert den alten Wert des Iterators zurück.


remove

Siehe auch	Algorithmus

Verschiebt die gewünschten Elemente an den Anfang des Containers und liefert einen Iterator zurück, der angibt, wo die Sequenz der gewünschten Elemente endet.

Syntax

#include <algorithm>

template <class ForwardIterator, class T>

ForwardIterator

remove (ForwardIterator first,

                         ForwardIterator last,

                         const T& value);

Beschreibung

Der Algorithmus remove entfernt alle Elemente, auf die sich der Iterator i in dem Bereich [first, last) bezieht, und für die die folgende Bedingung erfüllt ist: *i == Wert. remove gibt einen Iterator zurück, der das Ende des sich ergebenden Bereichs kennzeichnet. remove ist stabil, das heißt, die relative Ordnung der nicht entfernten Elemente ist die gleiche wie ihre relative Ordnung in dem ursprünglichen Bereich.

remove verringert nicht tatsächlich die Größe der Sequenz. Es verrichtet seine Arbeit, indem es: 1) die Werte, die beibehalten werden sollen, an den Anfang der Sequenz kopiert, und 2) einen Iterator zurückliefert, der angibt, wo die Sequenz der beizubehaltenden Werte endet. Elemente, die sich hinter dieser Iteratorenposition befinden, sind einfach die unveränderten Originalwerte. Dies ist ein einfaches Beispiel:

Angenommen, alle Werte "2" sollen aus der folgenden Sequenz entfernt werden:

354621271

Die Anwendung des Algorithmus remove ergibt dann die folgende Sequenz:

3546171|XX

Der senkrechte Strich stellt die Position des von remove zurückgelieferten Iterators dar. Beachten Sie, daß die Elemente links von dem senkrechten Strich die Originalsequenz darstellen, lediglich die "2en" sind entfernt.

Es werden exakt last1 - first1 Anwendungen des entsprechenden Prädikats ausgeführt.

Beispiel

 #include<algorithm>

 #include<vector>

 #include<iterator>

 using namespace std;

 template<class Arg>

 struct all_true : public unary_function<Arg, bool>

 {

   bool operator()(const Arg& x){ return 1; }

 };

 int main ()

 {

   int arr[10] = {1,2,3,4,5,6,7,8,9,10};

   vector<int> v(arr, arr+10);

   copy(v.begin(),v.end(),ostream_iterator<int>(cout," "));

   cout << endl << endl;

   // Entfernen der 7 (remove)

   vector<int>::iterator result =

           remove(v.begin(), v.end(), 7);

   // Löschen von Elementen aus dem Vektor

   v.erase(result, v.end());

   copy(v.begin(),v.end(),ostream_iterator<int>(cout," "));

   cout << endl << endl;

   // Alles nach dem vierten Element entfernen (remove)

   result = remove_if(v.begin()+4,

                      v.begin()+8, all_true<int>());

   // Löschen von Elementen

   v.erase(result, v.end());

   copy(v.begin(),v.end(),ostream_iterator<int>(cout," "));

   cout << endl << endl;

   return 0;

 }


remove_copy

Siehe auch	Algorithmus

Verschiebt die gewünschten Elemente an den Anfang eines Containers und gibt einen Iterator zurück, der angibt, wo die Sequenz der gewünschten Elemente endet.

Syntax

#include <algorithm>

template <class InputIterator,

          class OutputIterator,

          class T>

 OutputIterator remove_copy (InputIterator first,

                             InputIterator last,

                             OutputIterator result,

                             const T& value);

Beschreibung

Der Algorithmus remove_copy kopiert alle Elemente, auf die sich der Iterator i in dem Bereich [first, last) bezieht, und für die die folgende Bedingung nicht erfüllt ist: *i == Wert. remove_copy liefert das Ende des resultierenden Bereichs zurück. remove_copy ist stabil, d.h. die relative Ordnung in dem sich ergebenden Bereich ist die gleiche wie die relative Ordnung in dem ursprünglichen Bereich. Die Elemente in der Originalsequenz werden durch remove_copy nicht verändert.

Es werden exakt last1 - first1 Anwendungen des entsprechenden Prädikats ausgeführt.

Beispiel

/**************************************************************

 *

 * remove.cpp - Beispielprogramm für den Algorithmus remove.

*

 * $Id: remove.cpp,v 1.9 1995/10/06 20:59:40 hart Exp $

 *

 * $$RW_INSERT_HEADER "slyrs.str"

 *

 **************************************************************/

 #include <algorithm>

 #include <vector>

 #include <iterator>

 using namespace std;

 template<class Arg>

 struct all_true : public unary_function<Arg, bool>

 {

  bool operator() (const Arg&) { return 1; }

 };

 int main ()

 {

  int arr[10] = {1,2,3,4,5,6,7,8,9,10};

  vector<int> v(arr+0, arr+10);

  copy(v.begin(),v.end(),ostream_iterator<int>(cout," "));

  cout << endl << endl;

  //

  // Entfernen der 7.

  //

  vector<int>::iterator result = remove(v.begin(), v.end(), 7);

  //

  // Löschen der Elemente aus dem Vektor.

  //

  v.erase(result, v.end());

  copy(v.begin(),v.end(),ostream_iterator<int>(cout," "));

  cout << endl << endl;

  //

  // Alles hinter dem vierten Element entfernen (remove).

  //

  result = remove_if(v.begin()+4, v.begin()+8, all_true<int>());

  //

  // Löschen der betreffenden Elemente.

  //

  v.erase(result, v.end());

  copy(v.begin(),v.end(),ostream_iterator<int>(cout," "));

  cout << endl << endl;

  //

  // Nun alle 3en aus der Ausgabe entfernen.

  //

  remove_copy(v.begin(), v.end(),
        ostream_iterator<int>(cout," "), 3);

  cout << endl << endl;

  //

  // Jetzt alles aus der Ausgabe entfernen, was dem Prädikat genügt.

  // Es sollte sich ein NULL-Vektor ergeben.

  //

  remove_copy_if(v.begin(), v.end(),
         ostream_iterator<int>(cout," "),

         all_true<int>());

  return 0;

 }


remove_copy_if

Siehe auch	Algorithmus

Verschiebt die gewünschten Elemente an den Anfang eines Containers und liefert einen Iterator zurück, der beschreibt, wo die Sequenz der gewünschten Elemente endet.

Syntax

#include <algorithm>

template <class InputIterator,

          class OutputIterator,

          class Predicate>

 OutputIterator remove_copy_if (InputIterator first,

                                InputIterator last,

                                OutputIterator result,

                                Predicate pred);

Beschreibung

Der Algorithmus remove_copy_if kopiert alle Elemente, auf die sich der Iterator i in dem Bereich [first, last) bezieht und für die die folgende Bedingung nicht erfüllt ist: pred(*i) == true. remove_copy_if gibt das Ende des resultierenden Bereichs zurück. remove_copy_if ist stabil, das heißt, die relative Ordnung der Elemente in dem resultierenden Bereich ist die gleiche wie ihre relative Ordnung in dem ursprünglichen Bereich.

Es werden exakt last1 - first1 Anwendungen des entsprechenden Prädikats ausgeführt.

Beispiel

/**************************************************************

 *

 * remove.cpp - Beispielprogramm für den Algorithmus remove.

*

 * $Id: remove.cpp,v 1.9 1995/10/06 20:59:40 hart Exp $

 *

 * $$RW_INSERT_HEADER "slyrs.str"

 *

 **************************************************************/

 #include <algorithm>

 #include <vector>

 #include <iterator>

 using namespace std;

 template<class Arg>

 struct all_true : public unary_function<Arg, bool>

 {

  bool operator() (const Arg&) { return 1; }

 };

 int main ()

 {

  int arr[10] = {1,2,3,4,5,6,7,8,9,10};

  vector<int> v(arr+0, arr+10);

  copy(v.begin(),v.end(),ostream_iterator<int>(cout," "));

  cout << endl << endl;

  //

  // Entfernen der 7 (remove).

  //

  vector<int>::iterator result = remove(v.begin(), v.end(), 7);

  //

  // Elemente aus dem Vektor entfernen (erase).

  //

  v.erase(result, v.end());

  copy(v.begin(),v.end(),ostream_iterator<int>(cout," "));

  cout << endl << endl;

  //

  // Alles hinter dem vierten Element entfernen (remove).

  //

  result = remove_if(v.begin()+4, v.begin()+8, all_true<int>());

  //

  // Entsprechende Elemente löschen (erase).

  //

  v.erase(result, v.end());

  copy(v.begin(),v.end(),ostream_iterator<int>(cout," "));

  cout << endl << endl;

  //

  // Jetzt alle Dreien aus der Ausgabe entfernen.

  //

  remove_copy(v.begin(), v.end(),
        ostream_iterator<int>(cout," "), 3);

  cout << endl << endl;

  //

  // Jetzt alles aus der Ausgabe entfernen, was dem Prädikat genügt.

  // Es sollte sich ein NULL-Vektor ergeben.

  //

  remove_copy_if(v.begin(), v.end(),
         ostream_iterator<int>(cout," "),

         all_true<int>());

  return 0;

 }


remove_if

Siehe auch	Algorithmus

Verschiebt die gewünschten Elemente an den Anfang eines Containers und liefert einen Iterator zurück, der angibt, wo die Sequenz der gewünschten Elemente endet.

Syntax

#include <algorithm>

template <class ForwardIterator, class Predicate>

 ForwardIterator remove_if (ForwardIterator first,

                            ForwardIterator last,

                            Predicate pred);

Beschreibung

Der Algorithmus remove_if entfernt alle Elemente, auf die sich der Iterator i in dem Bereich [first, last) bezieht, und für die die folgende Bedingung erfüllt ist: pred(*i) == true. remove_if liefert das Ende des resultierenden Bereichs zurück. remove_if ist stabil, das heißt, die relative Ordnung der nicht entfernten Elemente ist die gleiche wie ihre relative Ordnung in dem ursprünglichen Bereich.

remove_if verringert nicht die tatsächliche Größe der Sequenz. Es verrichtet seine Arbeit, indem es: 1) die Werte, die beibehalten werden sollen, an den Anfang der Sequenz kopiert und 2) einen Iterator zurückliefert, der angibt, wo die Sequenz der zurückzuhaltenden Werte endet. Elemente, die hinter dieser Iteratorenposition stehen, sind einfach die unveränderten Originalwerte. Dies ist ein einfaches Beispiel:

Angenommen, es sollen alle geraden Zahlen aus der folgenden Sequenz entfernt werden:

123456789

Die Anwendung des Algorithmus remove_if ergibt dann die folgende Sequenz:

13579|XXXX

Der senkrechte Strich stellt die Position des von remove_if zurückgelieferten Iterators dar. Beachten Sie, daß die Elemente links von dem senkrechten Strich die Originalsequenz ohne die geraden Zahlen darstellen. Die Elemente rechts von dem Strich sind einfach nur die unveränderten Elemente der Originalsequenz.

Es werden exakt last1 - first1 Anwendungen des entsprechenden Prädikats ausgeführt.

Beispiel

 #include<algorithm>

 #include<vector>

 #include<iterator>

 using namespace std;

 template<class Arg>

 struct all_true : public unary_function<Arg, bool>

 {

   bool operator()(const Arg& x){ return 1; }

 };

 int main ()

 {

   int arr[10] = {1,2,3,4,5,6,7,8,9,10};

   vector<int> v(arr, arr+10);

   copy(v.begin(),v.end(),ostream_iterator<int>(cout," "));

   cout << endl << endl;

   // Entfernen der 7

   vector<int>::iterator result =

           remove(v.begin(), v.end(), 7);

   // Entsprechende Elemente aus dem Vektor löschen

   v.erase(result, v.end());

   copy(v.begin(),v.end(),ostream_iterator<int>(cout," "));

   cout << endl << endl;

   // Alles hinter dem vierten Element entfernen

   result = remove_if(v.begin()+4,

                      v.begin()+8, all_true<int>());

   // Löschen der entsprechenden Elemente

   v.erase(result, v.end());

   copy(v.begin(),v.end(),ostream_iterator<int>(cout," "));

   cout << endl << endl;

   return 0;

 }


replace

Siehe auch	Algorithmus

Ersetzt in einer Kollektion gespeicherte Werte durch neue Werte.

Syntax

#include <algorithm>

template <class ForwardIterator, class T>

void replace (ForwardIterator first,

              ForwardIterator last,

              const T& old_value,

              const T& new_value);

Beschreibung

Der Algorithmus replace ersetzt Elemente, auf die sich der Iterator i in dem Bereich [first, last) bezieht, durch new_value, wenn die folgende Bedingung erfüllt ist: *i == old_value

Es werden exakt last - first Vergleiche oder Anwendungen des entsprechenden Prädikats ausgeführt.

Beispiel

 #include<algorithm>

 #include<vector>

 #include<iterator>

 using namespace std;

 template<class Arg>

 struct all_true : public unary_function<Arg, bool>

 {

   bool operator()(const Arg&){ return 1; }

 };

 int main()

 {

   //Initialisieren eines Vektors mit einem Integer-Array

   int arr[10] = {1,2,3,4,5,6,7,8,9,10};

   vector<int> v(arr, arr+10);

   //Ausgeben des ursprünglichen Vektors

   cout << "The original list: " << endl << "     ";

   copy(v.begin(),v.end(),ostream_iterator<int>(cout," "));

   cout << endl << endl;

   //Die Zahl 7 durch 11 ersetzen

   replace(v.begin(), v.end(), 7, 11);

   // Den Vektor mit ersetzter 7 ausgeben,

   // 1 2 3 4 5 6 11 8 9 10

   cout << "List after replace " << endl << "     ";

   copy(v.begin(),v.end(),ostream_iterator<int>(cout," "));

   cout << endl << endl;

   // 1 2 3 durch 13 13 13 ersetzen

   replace_if(v.begin(), v.begin()+3, all_true<int>(), 13);

   // Den verbleibenden Vektor ausgeben,

   // 13 13 13 4 5 6 11 8 9 10

   cout << "List after replace_if " << endl << "     ";

   copy(v.begin(),v.end(),ostream_iterator<int>(cout," "));

   cout << endl << endl;

   return 0;

 }


replace_copy

Siehe auch	Algorithmus

Ersetzt in einer Kollektion gespeicherte Elemente durch neue Werte.

Syntax

#include <algorithm>

template <class InputIterator,

          class OutputIterator,

          class T>

OutputIterator replace_copy (InputIterator first,

                             InputIterator last,

                             OutputIterator result,

                             const T& old_value,

                             const T& new_value);

Beschreibung

Der Algorithmus replace_copy läßt die Originalsequenz unverändert und setzt die revidierte Sequenz in result ein. Der Algorithmus vergleicht Elemente, auf die sich der Iterator i in dem Bereich [first, last) bezieht, mit old_value. Wenn *i ungleich old_value ist, dann kopiert replace_copy *i in result+(first-i). Wenn *i==old_value, dann kopiert replace_copy new_value in result+(first-i). replace_copy liefert result+(last-first) zurück.

Es werden exakt last - first Vergleiche zwischen Werten ausgeführt.

Beispiel

/**************************************************************

 *

 * replace.cpp - Beispielprogramm für den Algorithmus replace

 *

 * $Id: replace.cpp,v 1.9 1995/10/06 21:01:02 hart Exp $

 *

 * $$RW_INSERT_HEADER "slyrs.str"

 *

 **************************************************************/

 #include <algorithm>

 #include <vector>

 #include <iterator>

 using namespace std;

 template<class Arg>

 struct all_true : public unary_function<Arg, bool>

 {

  bool operator() (const Arg&) { return 1; }

 };

 int main ()

 {

  //

  // Initialisieren eines Vektors mit einem Integer-Array.

  //

  int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };

  vector<int> v(arr+0, arr+10);

  //

  // Originalvektor ausgeben.

  //

  cout << "The original list: " << endl << "   ";

  copy(v.begin(), v.end(), ostream_iterator<int>(cout," "));

  cout << endl << endl;

  //

  // Die Zahl 7 durch 11 ersetzen.

  //

  replace(v.begin(), v.end(), 7, 11);

  //

  // Den Vektor mit ersetzter 7 ausgeben.

  //

  cout << "List after replace:" << endl << "   ";

  copy(v.begin(), v.end(), ostream_iterator<int>(cout," "));

  cout << endl << endl;

  //

  // 1 2 3 durch 13 13 13 ersetzen.

  //

  replace_if(v.begin(), v.begin()+3, all_true<int>(), 13);

  //

  // Den verbleibenden Vektor ausgeben.

  //

  cout << "List after replace_if:" << endl << "   ";

  copy(v.begin(), v.end(), ostream_iterator<int>(cout," "));

  cout << endl << endl;

  //

  // Die 13en in der Ausgabe durch 17en ersetzen.

  //

  cout << "List using replace_copy to cout:" << endl << "   ";

  replace_copy(v.begin(), v.end(),
        ostream_iterator<int>(cout, " "), 13, 17);

  cout << endl << endl;

  //

  // Ein einfaches Beispiel für replace_copy_if.

  //

  cout << "List w/ all elements output as 19s:" << endl << "  ";

  replace_copy_if(v.begin(), v.end(),
          ostream_iterator<int>(cout, " "),

          all_true<int>(), 19);

  cout << endl;

  return 0;

 }


replace_copy_if

Siehe auch	Algorithmus

Ersetzt in einer Kollektion gespeicherte Elemente durch neue Werte.

Syntax

#include <algorithm>

template <class InputIterator,

          class OutputIterator,

          class Predicate,

          class T>

OutputIterator replace_copy_if (InputIterator first,

                                InputIterator last,

                                OutputIterator result,

                                Predicate pred,

                                const T& new_value);

Beschreibung

Der Algorithmus replace_copy_if läßt die Originalsequenz unverändert und setzt eine revidierte Sequenz in result ein. Der Algorithmus vergleicht jedes Element *i in dem Bereich [first,last) mit den in pred angegebenen Bedingungen. Wenn pred(*i)==false ist, kopiert replace_copy_if *i in result+(first-i). Wenn pred(*i)==true ist, dann kopiert replace_copy new_value in result+(first-i). replace_copy_if liefert result+(last-first) zurück.

Es werden exakt last - first Anwendungen des Prädikats ausgeführt.

Beispiel

/**************************************************************

 *

 * replace.cpp - Beispielprogramm für den Algorithmus replace

 *

 * $Id: replace.cpp,v 1.9 1995/10/06 21:01:02 hart Exp $

 *

 * $$RW_INSERT_HEADER "slyrs.str"

 *

 **************************************************************/

 #include <algorithm>

 #include <vector>

 #include <iterator>

 using namespace std;

 template<class Arg>

 struct all_true : public unary_function<Arg, bool>

 {

  bool operator() (const Arg&) { return 1; }

 };

 int main ()

 {

  //

  // Initialisieren eines Vektors mit einem Integer-Array.

  //

  int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };

  vector<int> v(arr+0, arr+10);

  //

  // Ursprünglichen Vektor ausgeben.

  //

  cout << "The original list: " << endl << "   ";

  copy(v.begin(), v.end(), ostream_iterator<int>(cout," "));

  cout << endl << endl;

  //

  // Die Zahl 7 durch 11 ersetzen.

  //

  replace(v.begin(), v.end(), 7, 11);

  //

  // Den Vektor mit ersetzter 7 ausgeben.

  //

  cout << "List after replace:" << endl << "   ";

  copy(v.begin(), v.end(), ostream_iterator<int>(cout," "));

  cout << endl << endl;

  //

  // 1 2 3 durch 13 13 13 ersetzen.

  //

  replace_if(v.begin(), v.begin()+3, all_true<int>(), 13);

  //

  // Den verbleibenden Vektor ausgeben.

  //

  cout << "List after replace_if:" << endl << "   ";

  copy(v.begin(), v.end(), ostream_iterator<int>(cout," "));

  cout << endl << endl;

  //

  // Die 13en in der Ausgabe durch 17en ersetzen.

  //

  cout << "List using replace_copy to cout:" << endl << "   ";

  replace_copy(v.begin(), v.end(),
        ostream_iterator<int>(cout, " "), 13, 17);

  cout << endl << endl;

  //

  // Ein einfaches Beispiel für replace_copy_if.

  //

  cout << "List w/ all elements output as 19s:" << endl << "  ";

  replace_copy_if(v.begin(), v.end(),
          ostream_iterator<int>(cout, " "),

          all_true<int>(), 19);

  cout << endl;

  return 0;

 }


replace_if

Siehe auch	Algorithmus

Ersetzt in einer Kollektion gespeicherte Elemente durch neue Werte.

Syntax

#include <algorithm>

template <class ForwardIterator,

          class Predicate,

          class T>

void replace_if (ForwardIterator first,

                 ForwardIterator last,

                 Predicate pred

                 const T& new_value);

Beschreibung

Der Algorithmus replace_if ersetzt Elemente, auf die sich der Iterator i in dem Bereich [first, last) bezieht, durch new_value, wenn die folgende Bedingung erfüllt ist: pred(*i) == true.

Es werden exakt last - first Anwendungen des Prädikats ausgeführt.

Beispiel

 #include<algorithm>

 #include<vector>

 #include<iterator>

 using namespace std;

 template<class Arg>

 struct all_true : public unary_function<Arg, bool>

 {

   bool operator()(const Arg&){ return 1; }

 };

 int main()

 {

   //Initialisieren eines Vektors mit einem Integer-Array

   int arr[10] = {1,2,3,4,5,6,7,8,9,10};

   vector<int> v(arr, arr+10);

   //Ausgeben des ursprünglichen Vektors

   cout << "The original list: " << endl << "     ";

   copy(v.begin(),v.end(),ostream_iterator<int>(cout," "));

   cout << endl << endl;

   //Die Zahl 7 durch 11 ersetzen

   replace(v.begin(), v.end(), 7, 11);

   // Den Vektor mit ersetzter 7 ausgeben,

   // 1 2 3 4 5 6 11 8 9 10

   cout << "List after replace " << endl << "     ";

   copy(v.begin(),v.end(),ostream_iterator<int>(cout," "));

   cout << endl << endl;

   // 1 2 3 durch 13 13 13 ersetzen

   replace_if(v.begin(), v.begin()+3, all_true<int>(), 13);

   // Den verbleibenden Vektor ausgeben,

   // 13 13 13 4 5 6 11 8 9 10

   cout << "List after replace_if " << endl << "     ";

   copy(v.begin(),v.end(),ostream_iterator<int>(cout," "));

   cout << endl << endl;

   return 0;

 }


return_temporary_buffer

Siehe auch	Primitiv zur Speicherverwaltung

Zeiger-basiertes Primitiv für die Speicherbehandlung.

Syntax

#include <memory>

template <class T>

 void return_temporary_buffer (T* p, T*);

Beschreibung

Das Funktions-Template return_temporary_buffer liefert einen Puffer, der zuvor durch get_temporary_buffer alloziiert worden war, an den verfügbaren Arbeitsspeicher zurück. Der Parameter p zeigt auf den Puffer.


reverse

Siehe auch	Algorithmus

Kehrt die Reihenfolge von Elementen in einer Kollektion um.

Syntax

#include <algorithm>

template <class BidirectionalIterator>

 void reverse (BidirectionalIterator first,

               BidirectionalIterator last);

Beschreibung

Der Algorithmus reverse kehrt die Elemente in einer Sequenz um, so daß das letzte Element zum neuen ersten Element wird, und das erste Element zum neuen letzten. Für jeden nicht negativen Integer i <= (last - first)/2 , wendet reverse die Operation swap auf alle Iteratorenpaare first + i,(last - I)-1 an.

Da reverse von bidirektionalen Iteratoren ausgeht, gibt es keinen Rückgabewert.

reverse führt exakt (last - first)/2 Vertauschungen aus.

Beispiel

 #include<algorithm>

 #include<vector>

 using namespace std;

 int main()

 {

   //Initialisieren eines Vektors mit einem Integer-Array.

   int arr[10] = {1,2,3,4,5,6,7,8,9,10};

   vector<int> v(arr, arr+10);

   //Elemente in der ursprünglichen (sortierten) Reihenfolge ausgeben

   cout << "Elements before reverse: " << endl << "     ";

   copy(v.begin(),v.end(),ostream_iterator<int>(cout," "));

   cout << endl << endl;

   //Umkehren der Ordnung der Elemente

   reverse(v.begin(), v.end());

   //Ausgeben der umgekehrten Elemente

   cout << "Elements after reverse: " << endl << "     ";

   copy(v.begin(),v.end(),ostream_iterator<int>(cout," "));

   cout << endl;

   return 0;

 }


reverse_bidirectional_iterator, reverse_iterator

Siehe auch	Iterator

Ein Iterator, der eine Kollektion in umgekehrter Richtung durchläuft.

Syntax

#include <iterator>

template <class BidirectionalIterator,

          class T,

          class Reference = T&,

          class Distance = ptrdiff_t>

class reverse_bidirectional_iterator

   : public bidirectional_iterator<T, Distance>  {

  protected:

    BidirectionalIterator current;

  public:

    reverse_bidirectional_iterator ();

    explicit reverse_bidirectional_iterator

      (BidirectionalIterator x);

    BidirectionalIterator base ();

    Reference operator* ();

    reverse_bidirectional_iterator <BidirectionalIterator,

                                    T,

                                    Reference,

                                    Distance> &

      operator++ ();

    reverse_bidirectional_iterator <BidirectionalIterator,

                                    T,

                                    Reference,

                                    Distance>

      operator++ (int);

    reverse_bidirectional_iterator <BidirectionalIterator,

                                    T,

                                    Reference,

                                    Distance> &

      operator-- ();

    reverse_bidirectional_iterator <BidirectionalIterator,

                                    T,

                                    Reference,

                                    Distance>

      operator-- (int);

  };

  template <class BidirectionalIterator,

            class T,

            class Reference,

            class Distance>

  bool operator== (

     const reverse_bidirectional_iterator

        <BidirectionalIterator,

         T,

         Reference,

         Distance>& x,

      const reverse_bidirectional_iterator

        <BidirectionalIterator,

         T,

         Reference,

         Distance>& y);

template <class RandomAccessIterator,

          class T,

          class Reference = T&,

          class Distance = ptrdiff_t>

class reverse_iterator

   : public random_access_iterator<T, Distance>  {

  protected:

    RandomAccessIterator current;

  public:

    reverse_iterator ();

    explicit reverse_iterator (RandomAccessIterator x);

    RandomAccessIterator base ();

    Reference operator* ();

    reverse_iterator <RandomAccessIterator,

                      T,

                      Reference,

                      Distance> &

      operator++ ();

    reverse_iterator <RandomAccessIterator,

                      T,

                      Reference,

                      Distance>

      operator++ (int);

    reverse_iterator <RandomAccessIterator,

                      T,

                      Reference,

                      Distance> &

      operator-- ();

    reverse_iterator <RandomAccessIterator,

                      T,

                      Reference,

                      Distance>

      operator-- (int);

    reverse_iterator <RandomAccessIterator,

                      T,

                      Reference,

                      Distance>

      operator+ (Distance n) const;

    reverse_iterator <RandomAccessIterator,

                      T,

                      Reference,

                      Distance> &

      operator+= (Distance n);

    reverse_iterator <RandomAccessIterator,

                      T,

                      Reference,

                      Distance>

      operator- (Distance n) const;

    reverse_iterator <RandomAccessIterator,

                      T,

                      Reference,

                      Distance> &

      operator-= (Distance n);

    Reference operator[] (Distance n);

    template <class RandomAccessIterator,

              class T,

              class Reference,

              class Distance> bool operator== (

        const reverse_iterator

          <RandomAccessIterator,

           T,

           Reference,

           Distance>& x,

        const reverse_iterator

          <RandomAccessIterator,

           T,

           Reference,

           Distance>& y);

    template <class RandomAccessIterator,

              class T,

              class Reference,

              class Distance> bool operator< (

        const reverse_iterator

          <RandomAccessIterator,

           T,

           Reference,

           Distance>& x,

        const reverse_iterator

          <RandomAccessIterator,

           T,

           Reference,

           Distance>& y);

    template <class RandomAccessIterator,

              class T,

              class Reference,

              class Distance> Distance operator- (

        const reverse_iterator

          <RandomAccessIterator,

           T,

           Reference,

           Distance>& x,

        const reverse_iterator

          <RandomAccessIterator,

           T,

           Reference,

           Distance>& y);

    template <class RandomAccessIterator,

              class T,

              class Reference,

              class Distance>

    reverse_iterator<RandomAccessIterator,

                     T,

                     Reference,

                     Distance> operator+ (

        Distance n,

        const reverse_iterator

          <RandomAccessIterator,

           T,

           Reference,

           Distance>& x);

};

Beschreibung

Die Iteratoren reverse_iterator und reverse_bidirectional_iterator entsprechen random_access_iterator und bidirectional_iterator mit der Ausnahme, daß sie die Kollektionen, auf die sie zeigen, in umgekehrter Richtung durchlaufen. Die grundlegende Relation zwischen einem Reverse-Iterator und dem ihm entsprechenden Iterator i wird durch die Gleichsetzung:

&*(reverse_iterator(i)) == &*(i-1);

wiedergegeben. Diese Zuordnung ist durch die Tatsache erzwungen, daß es zwar immer einen Zeiger hinter dem Ende eines Containers gibt, aber unter Umständen keinen gültigen Zeiger vor dessen Anfang.

Die folgenden Bedingungen sind für reverse_bidirectional_iterators erfüllt:

{bmc. bullet.bmp}	Diese Iteratoren können mit dem Standardkonstruktor oder durch einen Konstruktor mit einem einzelnen Argument instantiiert werden, der den neuen reverse_bidirectional_iterator mit einem bidirectional_iterator initialisiert.

operator* liefert eine Referenz auf den Wert zurück, auf den aktuell gezeigt wird.

operator++ bewegt den Iterator auf das vorhergehende Element weiter (--current) und gibt eine Referenz auf *this zurück.

operator++(int) bewegt den Iterator auf das vorhergehende Element (--current) weiter und liefert den alten Wert von *this zurück.

operator-- bewegt den Iterator auf das folgende Element (++current) weiter und liefert eine Referenz auf *this zurück.

operator--(int) bewegt den Iterator auf das folgende Element weiter (++current) und liefert den alten Wert von *this zurück.

operator== Dieser Nicht-Element-Operator gibt true zurück, wenn die Iteratoren x und y auf das gleiche Element zeigen.

Die folgenden Bedingungen sind für reverse__iteratoren erfüllt:

Diese Iteratoren können mit dem Standardkonstruktor oder alternativ durch einen Konstruktor mit einem einzelnen Element instantiiert werden, der den neuen reverse_iterator mit einem random_access_iterator initialisiert.

operator* liefert eine Referenz auf den Wert zurück, auf den aktuell gezeigt wird.

operator++ bewegt den Iterator auf das vorhergehende Element (--current) weiter und gibt eine Referenz auf *this zurück.

operator++(int) bewegt den Iterator auf das vorangegangene Element (--current) weiter und gibt den alten Wert von *this zurück.

operator-- bewegt den Iterator auf das folgende Element weiter (++current) und liefert eine Referenz auf *this zurück.

operator--(int) bewegt den Iterator auf das folgende Element weiter (++current) und liefert den alten Wert von *this zurück.

operator== ist ein Nicht-Element-Operator, der true zurückgibt, wenn die Iteratoren x und y auf das gleiche Element  zeigen.

Die übrigen Operatoren (<, +, - , +=, -=) werden umdefiniert, damit sie sich genauso verhalten wie in einem random_access_iterator, mit Ausnahme der umgekehrten Richtung.

Alle Iteratoroperationen dürfen im Höchstfall die amortisierte konstante Ausführungszeit beanspruchen.

Beispiel

 #include<iterator>

 #include<vector>

 using namespace std;

 int main()

 {

   //Initialisieren eines Vektors mittels eines Arrays

   int arr[4] = {3,4,7,8};

   vector<int> v(arr,arr+4);

   //Ausgeben des ursprünglichen Vektors

   cout << "Traversing vector with iterator: " << endl << "     ";

   for(vector<int>::iterator i = v.begin(); i != v.end(); i++)

     cout << *i << " ";

   //Deklarieren des reverse_iterator

   vector<int>::reverse_iterator rev(v.end());

   vector<int>::reverse_iterator rev_end(v.begin());

   //Den Vektor rückwärts ausgeben

   cout << endl << endl;

   cout << "Same vector, same loop, reverse_itertor: " << endl
        << "     ";

   for(; rev != rev_end; rev++)

     cout << *rev << " ";

   return 0;

 }


reverse_copy

Siehe auch	Algorithmus

Kehrt die Reihenfolge der Elemente in einer Kollektion um und kopiert sie dabei in eine neue Kollektion.

Syntax

#include <algorithm>

template <class BidirectionalIterator, class OutputIterator>

 OutputIterator reverse_copy (BidirectionalIterator first,

                              BidirectionalIterator last,

                              OutputIterator result);

Beschreibung

Der Algorithmus reverse_copy kopiert den Bereich [first, last) in den Bereich [result, result + (last - first)), so daß für jeden nicht negativen Integer i < (last - first) die folgende Zuweisung vorgenommen wird:

 *(result + (last - first) -i) = *(first + i)

reverse_copy liefert result + (last - first) zurück. Die Bereiche [first, last) und [result, result + (last - first)) dürfen nicht überlappen.

reverse_copy führt exakt (last - first) Zuweisungen aus.

Beispiel

/***************************************************************

 *

 * reverse.cpp - Beispielprogramm für Reverse-Algorithmen.

 *        See Class Reference Section

 *

 * $Id: reverse.cpp,v 1.7 1995/10/06 19:35:37 hart Exp $

 *

 * $$RW_INSERT_HEADER "slyrs.str"

 *

 **************************************************************/

 #include <algorithm>

 #include <vector>

 using namespace std;

 int main ()

 {

  //

  // Initialisiert einen Vektor mit einem Integer-Array.

  //

  int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };

  vector<int> v(arr+0, arr+10);

  //

  // Elemente in der ursprünglichen (sortierten) Reihenfolge ausgeben.

  //

  cout << "Elements before reverse: " << endl << "   ";

  copy(v.begin(), v.end(), ostream_iterator<int>(cout," "));

  cout << endl << endl;

  //

  // Reihenfolge umkehren.

  //

  reverse(v.begin(), v.end());

  //

  // Umgekehrte Elemente ausgeben.

  //

  cout << "Elements after reverse: " << endl << "   ";

  copy(v.begin(), v.end(), ostream_iterator<int>(cout," "));

  cout << endl << endl;

  cout << "A reverse_copy to cout: " << endl << "   ";

  reverse_copy(v.begin(), v.end(),
        ostream_iterator<int>(cout, " "));

  cout << endl;

  return 0;

 }


reverse_iterator

Siehe dazu den Abschnitt reverse_bidirectional_iterator dieses Referenzteils.


rotate, rotate_copy

Algorithmus

Rotiert die Reihenfolge der Elemente in einer Kollektion nach links (gegen den Uhrzeigersinn), so daß das erste Element am Ende steht, das zweite Element an erster Stelle usw., bis das Element, auf das der angegebene Iterator zeigt, das erste Element in der Kollektion ist.

Syntax

#include <algorithm>

template <class ForwardIterator>

void rotate (ForwardIterator first,

             ForwardIterator middle,

             ForwardIterator last);

template <class ForwardIterator, class OutputIterator>

OutputIterator rotate_copy (ForwardIterator first,

                            ForwardIterator middle,

                            ForwardIterator last,

                            OutputIterator result);

Beschreibung

Der Algorithmus rotate nimmt drei Iteratorenargumente an, first, das den Anfang einer Sequenz definiert, last, das das Ende der Sequenz angibt, und middle, das einen Punkt innerhalb der Sequenz bezeichnet. rotate "vertauscht" das Segment, das Elemente first bis middle-1 enthält, mit dem Segment, das die Elemente von middle bis last enthält. Nach der Anwendung von rotate befindet sich das Element, das an der Position middle stand, an der Position first, und die übrigen Elemente in diesem Segment weisen die gleiche relative Ordnung zueinander auf wie zuvor. In ähnlicher Weise befindet sich das Element, das zuvor Position first einnahm, nun an der Position last-middle +1. Ein Beispiel soll die Arbeitsweise von rotate verdeutlichen:

Es soll von dieser Sequenz ausgegangen werden:

2 4 6 8 1 3 5

Wenn nun rotate mit middle=5 aufgerufen wird, sind die zwei Segmente

2 4 6 8 and 1 3 5

Nach der Anwendung von rotate ergibt sich die neue Sequenz:

1 3 5 2 4 6 8

Beachten Sie, daß das Element, das sich an der fünften Position befand, nun an der ersten steht, während das Element aus der ersten Position sich jetzt an Position 4 befindet (last - first +1, oder 8 - 5 +1 =4).

Die formale Beschreibung dieses Algorithmus lautet: für jeden nicht negativen Integer i < (last - first) plaziert rotate das Element an der Position first + i an der Position first + (i + (last - middle)) % (last - first). [first, middle) und [middle, last) sind gültige Bereiche.

rotate_copy rotiert die Elemente wie oben angegeben. Statt aber Elemente innerhalb der gleichen Kollektion zu vertauschen, kopiert es das Ergebnis der Rotation in einen Container, der in result angegeben ist. rotate_copy kopiert den Bereich [first, last) in den Bereich [result, result + (last - first)), so daß für jeden nicht negativen Integer i < (last - first) die folgende Zuweisung vorgenommen wird:

*(result  +  (i  +  (last - middle)) % (last -first)) = *(first +  i).

Die Bereiche [first, last) und [result, result, + (last - first)) dürfen nicht überlappen.

Für rotate werden höchstens last - first Vertauschungen ausgeführt.

Für rotate_copy werden höchstens last - first Zuweisungen vorgenommen.

Beispiel

 #include<algorithm>

 #include<vector>

 using namespace std;

 int main()

 {

   //Initialisieren eines Vektors mit einem Integer-Array

   int arr[10] = {1,2,3,4,5,6,7,8,9,10};

   vector<int> v(arr, arr+10);

   //Elemente in der ursprünglichen (sortierten) Reihenfolge ausgeben

   cout << "Elements before rotate: " << endl << "     ";

   copy(v.begin(),v.end(),ostream_iterator<int>(cout," "));

   cout << endl << endl;

   //Elemente rotieren

   rotate(v.begin(), v.begin()+4, v.end());

   //Rotierte Elemente ausgeben

   cout << "Elements after rotate: " << endl << "     ";

   copy(v.begin(),v.end(),ostream_iterator<int>(cout," "));

   cout << endl;

   return 0;

 }


search

Algorithmus

Sucht eine Untersequenz innerhalb einer Sequenz von Werten, die elementweise mit den Werten in einem angegebenen Bereich übereinstimmt.

Syntax

#include <algorithm>

template <class ForwardIterator1, class ForwardIterator2>

 ForwardIterator1 search (ForwardIterator1 first1,

                          ForwardIterator1 last1,

                          ForwardIterator2 first2,

                          ForwardIterator2 last2);

template <class ForwardIterator1,

          class ForwardIterator2,

          class BinaryPredicate>

 ForwardIterator1 search (ForwardIterator1 first1,

                          ForwardIterator1 last1,

                          ForwardIterator2 first2,

                          ForwardIterator2 last2,

                          BinaryPredicate binary_pred);

Beschreibung

Der Algorithmus search sucht nach einer Untersequenz (first2, last2) innerhalb einer Sequenz (first1, last1) und liefert die Anfangsposition der Untersequenz zurück. Wenn die Untersequenz nicht gefunden wird, gibt search last1 zurück. Die erste Fassung von search verwendet den Gleichheitsoperator (==) als Voreinstellung, die zweite Fassung erlaubt die Angabe eines binären Prädikats für die Durchführung des Vergleichs.

search führt höchstens (last1 - first1)*(last2-first2) Anwendungen des entsprechenden Prädikats aus.

Beispiel

 #include<algorithm>

 #include<list>

 using namespace std;

 int main()

 {

   // Initialisieren einer Listensequenz und

   // Untersequenz mit Zeichen

   char seq[40] = "Here's a string with a substring in it";

   char subseq[10] = "substring";

   list<char> sequence(seq, seq+39);

   list<char> subseqnc(subseq, subseq+9);

   //Ausgeben der ursprünglichen Sequenz

   cout << endl << "The subsequence, " << subseq

        << ", was found at the ";

   cout << endl << "location identified by a '*'"

        << endl << "     ";

   // Erzeugen eines Iterators, um die Position der

   // Untersequenz innerhalb der Sequenz anzugeben

   list<char>::iterator place;

   //Suche ausführen

   place = search(sequence.begin(), sequence.end(),

                  subseqnc.begin(), subseqnc.end());

   //Ergebnis durch Markieren des ersten Zeichens mit einem '*' kennzeichnen

   *place = '*';

   //Sequenz ausgeben, um das Ergebnis anzuzeigen

   for(list<char>::iterator i = sequence.begin();

           i != sequence.end(); i++)

     cout << *i;

   cout << endl;

   return 0;

 }


Sequenz

Eine Sequenz ist ein Container, der eine Menge von Objekten gleichen Typs in einer linearen Anordnung organisiert. vector, list, deque und string gehören in diese Kategorie.

Sequenzen bieten verschiedene Kompromisse zwischen unterschiedlichen Eigenschaften. vector erlaubt schnelle Einfüge- und Löschvorgänge am Ende des Containers. deque ist dann sinnvoll, wenn Einfüge- und Löschvorgänge am Anfang oder am Ende der Sequenz vorgenommen werden. list sollte verwendet werden, wenn häufig Einfüge- oder Löschoperationen in der Mitte der Sequenz erforderlich sind.


set

Siehe auch	Container

Ein assoziativer Container, der eindeutige Indizes unterstützt.

Syntax

#include <set>

template <class Key, class Compare = less<Key>>

 class set {

public:

 // Typen

   typedef Key key_type;

   typedef Key value_type;

   typedef typename reference;

   typedef typename const_reference;

   typedef Compare key_compare;

   typedef Compare value_compare;

   typedef typename iterator;

   typedef typename const_iterator;

   typedef typename size_type;

   typedef difference_type;

   typedef reverse_iterator<iterator, value_type,

           reference, difference_type> reverse_iterator;

   typedef const_reverse_iterator<const_iterator,

           value_type, reference  difference_type>

           const_reverse_iterator;

 // Konstruieren/Kopieren/Entfernen

   explicit set (const Compare& = Compare());

   template <class InputIterator>

    set (InputIterator, InputIterator, const Compare& = Compare());

   set (const set<Key, Compare>&);

   ~set ();

   set<Key, Compare>& operator= (const set Key, Compare>&);

 // Iteratoren

   iterator begin () const;

   iterator end () const;

   reverse_iterator rbegin ();

   reverse_iterator rend ();

 // Kapazität

   bool empty () const;

   size_type size () const;

   size_type max_size () const;

 // Modifizierer

   pair<iterator, bool> insert (const value_type&);

   iterator insert (iterator, const value_type&);

   template <class InputIterator>

    void insert (iterator, InputIterator, InputIterator);

   void erase (iterator);

   size_type erase (const key_type&);

   void erase (iterator, iterator);

   void swap (set<Key, Compare>&);

 // Observer

   key_compare key_comp () const;

   value_compare value_comp () const;

 // Mengenoperationen

   size_type count (const key_type&) const;

   pair<iterator, iterator> equal_range (const  key_type&) const;

   iterator find (const key_value&) const;

   iterator lower_bound (const key_type&) const;

   iterator upper_bound (const key_type&) const

};

 // Vergleich

template <class Key, class Compare>

 bool operator== (const set<Key, Compare>& x,

                  const set <Key,Compare>& y);

template <class Key, class Compare>

 bool operator< (const set <Key, Compare>& x,

                 const set <Key, Compare>& y);

Beschreibung

set<T,Compare> ist ein assoziativer Containertyp, der eindeutige Indizes (Schlüssel) unterstützt und ein schnelles Auffinden dieser Indizes ermöglicht. Ein Set enthält jeden Index höchstens einmal. Die Indizes werden mit Compare sortiert.

Da ein Set eine Über-Alles-Ordnung ihrer Elemente aufrechterhält, können die Indexwerte nicht direkt geändert werden. Statt dessen müssen neue Elemente mit einem Einfügeiterator (insert_iterator) hinzugefügt werden.

Jeder Typ, der für den Template-Parameter Key verwendet wird, muß den folgenden Bedingungen genügen (dabei ist T der Typ, t ein Wert von T und u ist ein const value von T):

  Kopierkonstruktoren   T(t) und T(u)

  Destruktor            t.~T()

  Adresse von           &t und &u, was T* beziehungsweise

                        const T* ergibt

  Zuweisung             t = a; dabei ist a ein

                        (unter Umständen const-) Wert von T

Der Typ, der für den Template-Parameter Compare verwendet wird, muß den Anforderungen für binäre Funktionen entsprechen.

Beschränkungen

In allen Containern der Standardbibliothek werden Elementfunktions-Templates verwendet. Ein Beispiel für dieses Merkmal ist der Konstruktor für Set <Key,Compare>, der zwei Iterator-Templates annimmt:

template <class InputIterator>

 set (InputIterator, InputIterator);

set verfügt darüber hinaus auch über eine Einfügefunktion dieses Typs. Diese Funktionen erlauben - sofern das nicht durch Compiler-Beschränkungen verhindert wird - die Verwendung jedes beliebigen Typs von Eingabeiterator als Argument. Für Compiler, die dieses Merkmal nicht unterstützen, stehen Ersatzfunktionen zur Verfügung. Sie erlauben die Verwendung eines Iterators, der aus dem gleichen Container-Typ wie dem konstruierten (bzw. dem, für den eine Elementfunktion aufgerufen wird) stammt. Alternativ kann ein Zeiger auf den im Container enthaltenen Elementtyp eingesetzt werden.

Wenn der verwendete Compiler keine Elementfunktions-Templates unterstützt, kann eine Menge (set) beispielsweise auf eine der folgenden Arten konstruiert werden:

int intarray[10];

set<int,less<int> > first_set(intarray,intarray + 10);

set<int, less<int> >

second_set(first_set.begin(),first_set.end());

nicht jedoch auf diese Weise:

set<long, less<long> >

long_set(first_set.begin(),first_set.end());

da long_set und first_set nicht vom gleichen Typ sind.

Ferner unterstützen viele Compiler keine voreingestellten Template-Argumente. Wenn das auch für den verwendeten Compiler zutrifft, muß das Template-Argument Compare stets angegeben werden.

Beispiel

 #include <set>

 using namespace std;

 typedef set<double,less<double> > set_type;

 ostream& operator<<(ostream& out, const set_type& s)

 {

   copy(s.begin(), s.end(),

        ostream_iterator<set_type::value_type>(cout," "));

   return out;

 }

 int main(void)

 {

   // Eine Menge mit Doubles und eine Menge mit Integern erzeugen

   set_type   sd;

   int         i;

   for(i = 0; i < 10; ++i) {

     // Werte einfügen

     sd.insert(i);

   }

   // die Menge ausgeben

   cout << sd << endl << endl;

   // jetzt die Hälfte der Elemente in der Menge entfernen

   int      half = sd.size() >> 1;

   set_type::iterator sdi = sd.begin();

   advance(sdi,half);

   sd.erase(sd.begin(),sdi);

   // erneut ausgeben

   cout << sd << endl << endl;

   // Eine weitere Menge und eine leere Ergebnismenge erzeugen

   set_type sd2, sdResult;

   for (i = 1; i < 9; i++)

      sd2.insert(i+5);

   cout << sd2 << endl;

   // Zwei Mengenalgorithmen ausprobieren

   set_union(sd.begin(),sd.end(),sd2.begin(),sd2.end(),

          inserter(sdResult,sdResult.begin()));

   cout << "Union:" << endl << sdResult << endl;

   sdResult.erase(sdResult.begin(),sdResult.end());

   set_intersection(sd.begin(),sd.end(),

          sd2.begin(),sd2.end(),

          inserter(sdResult,sdResult.begin()));

   cout << "Intersection:" << endl << sdResult << endl;

   return 0;

 }

Konstruktoren und Destruktoren

explicit

set (const Compare& comp = Compare());

Der Standardkonstruktor. Erzeugt ein Set mit null Elementen. Wenn das Funktionsobjekt comp angegeben wird, wird es für den Vergleich der Elemente des Sets verwendet. Andernfalls wird das voreingestellte Funktionsobjekt im Template-Argument verwendet. Das Template-Argument ist standardmäßig kleiner als (<, less).

template <class InputIterator>

set (InputIterator first, InputIterator last, const Compare& comp = Compare ());

Erzeugt ein Set von der Länge last - first, die mit allen Werte gefüllt ist, die durch Dereferenzieren der InputIterators für den Bereich [first, last) erhalten werden. Wenn das Funktionsobjekt comp angegeben wird, wird es verwendet, um die Elemente des Sets zu vergleichen. Andernfalls wird das voreingestellte Funktionsobjekt im Template-Argument verwendet. Das Template-Argument ist standardmäßig kleiner als (<,less).

set (const set<Key, Compare>& x);

Kopierkonstruktor. Erzeugt eine Kopie von x.

~set ();

Destruktor. Gibt den gesamten für self zugewiesenen Speicher frei.

Zuweisungsoperator

set<Key, Compare>&

operator= (const set Key, Compare>& x);

Zuweisungsoperator. self teilt sich eine Implementation mit x. Liefert eine Referenz auf self zurück.

Iteratoren

iterator begin ();

Liefert einen Iterator zurück, der auf das erste Element in self zeigt.

const_iterator begin () const;

Liefert einen const_iterator zurück, der auf das erste Element in self zeigt.

iterator end ();

Liefert einen Iterator zurück, der auf den Nach-Bereichsende-Wert zeigt.

const_iterator end () const;

Liefert einen const_iterator zurück, der auf den Nach-Bereichsende-Wert zeigt.

reverse_iterator rbegin ();

Liefert einen reverse_iterator zurück, der auf den Nach-Bereichsende-Wert zeigt.

const_reverse_iterator rbegin () const;

Liefert einen const_reverse_iterator zurück, der auf den Nach-Bereichsende-Wert zeigt.

reverse_iterator rend ();

Liefert einen reverse_iterator zurück, der auf das erste Element zeigt.

const_reverse_iterator rend () const;

Liefert einen const_reverse_iterator zurück, der auf das erste Element zeigt.

Öffentliche Elementfunktionen

size_type

count (const key_type& x) const;

Gibt die Anzahl der Elemente zurück, die gleich x sind. Da ein Set nur eindeutige Indizes unterstützt, gibt count immer 1 zurück.

bool

empty () const;

Gibt true zurück, wenn die Größe null ist.

pair<iterator, iterator>

equal_range (const key_type&  x) const;

Liefert das Paar pair<lower_bound(),upper_bound()> zurück. Die Funktion equal_range zeigt den gültigen Bereich für die Einfügung von x in das Set an.

void

erase (iterator position);

Entfernt das Element, auf das position zeigt.

size_type

erase (const key_type& x);

Entfernt alle Elemente, die mit x übereinstimmen. Gibt die Anzahl der entfernten Elemente zurück. Da Sets nur eindeutige Indizes unterstützen, gibt erase immer 1 zurück.

void

erase (iterator first, iterator last);

Entfernt die Elemente in dem Bereich [first, last).

iterator

find (const key_value& x) const;

Liefert einen Iterator zurück, der auf das Element zeigt, das gleich x ist. Wenn kein solches Element vorhanden ist, zeigt der Iterator auf den Nach-Bereichsende-Wert.

pair<iterator, bool>

insert (const value_type& x);

Fügt x in self ein, nach Maßgabe des Vergleichs-Funktionsobjekts. Das Standard-Vergleichs-Funktionsobjekt des Templates ist less (<).

iterator

insert (iterator position, const value_type& x);

Fügt x vor der Position position ein. Der Rückgabewert zeigt auf das eingefügte x.

template <class InputIterator>

void

insert(iterator position, InputIterator first,
       InputIterator last);

Fügt Kopien des Elements in den Bereich [first, last] vor position ein.

key_compare

key_comp () const;

Liefert das Vergleichs-Funktionsobjekt für das Set zurück.

iterator

lower_bound (const key_type& x) const;

Liefert einen Iterator zurück, der auf das erste Element zeigt, das größer als oder gleich x ist. Wenn kein solches Element vorhanden ist, zeigt der Iterator auf den Nach-Bereichsende-Wert.

size_type

max_size () const;

Liefert die Größe size () des größten möglichen Sets zurück.

size_type

size () const;

Liefert die Anzahl der Elemente zurück.

void

swap (set<Key, Compare>& x);

Tauscht self gegen x aus.

iterator

upper_bound (const key_type& x) const

Liefert einen Iterator zurück, der auf das erste Element zeigt, das größer als x ist. Wenn kein solches Element vorhanden ist, zeigt der Iterator auf den Nach-Bereichsende-Wert.

value_compare

value_comp () const;

Liefert das Vergleichsobjekt des Sets zurück.

Globale Operatoren

template <class Key, class Compare>

 bool operator== (const set<Key, Compare>& x,

                  const set<Key, Compare>& y);

Gleichheitsoperator. Gibt true zurück, wenn x gleich y ist.

template <class Key, class Compare>

bool operator< (const set <Key, Compare>& x,

                const set <Key, Compare>& y);

Liefert true zurück, wenn die in x enthaltenen Elemente lexikalisch weniger sind als die in y enthaltenen Elemente.


set_difference

Siehe auch	Algorithmus

Grundlegende Mengenoperation für sortierte Sequenzen.

Syntax

#include <algorithm>

template <class InputIterator1, class InputIterator2,

          class OutputIterator>

OutputIterator

set_difference (InputIterator1 first1, InputIterator1 last1,

                InputIterator2 first2, InputIterator2 last2,

                OutputIterator result);

template <class InputIterator1, class InputIterator2,

          class OutputIterator, class Compare>

OutputIterator

set_difference (InputIterator1 first1, InputIterator1 last1,

                InputIterator2 first2, InputIterator2 last2,

                OutputIterator result, Compare comp);

Beschreibung

Der Algorithmus set_difference konstruiert eine sortierte Differenzmenge, die Kopien der Elemente enthält, die im Bereich [first1, last1) enthalten sind, jedoch nicht im Bereich [first2, last2). Er liefert das Ende des konstruierten Bereichs zurück.

Es soll beispielsweise von den beiden folgenden Sets ausgegangen werden:

1 2 3 4 5

und

3 4 5 6 7

Das Ergebnis der Anwendung von set_difference ist dann das Set:

1 2

Das Ergebnis von set_difference ist nicht definiert, wenn der Ergebnisbereich Überschneidungen mit einem der beiden Ursprungsbereiche aufweist.

set_difference geht davon aus, daß die Bereiche unter Verwendung des voreingestellten Vergleichsoperators kleiner als (<) sortiert wurden, es sei denn, es wurde ein alternativer Vergleichsoperator (comp) angegeben.

Verwenden Sie den Algorithmus set_symmetric_difference, wenn ein Ergebnis zurückgeliefert werden soll, das alle Elemente enthält, die in einer und nur in einer der beiden Sets auftreten.

Es werden höchstens ((last1 - first1) + (last2 - first2)) * 2 -1 Vergleiche ausgeführt.

Beispiel

 #include<algorithm>

 #include<set>

 using namespace std;

 int main()

 {

   //Einige Mengen initialisieren

   int a1[10] = {1,2,3,4,5,6,7,8,9,10};

   int a2[6]  = {2,4,6,8,10,12};

   set<int, less<int> > all(a1, a1+10), even(a2, a2+6),

                        odd;

   //Einen insert_iterator für ungerade Elemente erzeugen

   insert_iterator<set<int, less<int> > >

                 odd_ins(odd, odd.begin());

   // set_difference demonstrieren

   cout << "The result of:" << endl << "{";

   copy(all.begin(),all.end(),

        ostream_iterator<int>(cout," "));

   cout << "} - {";

   copy(even.begin(),even.end(),

        ostream_iterator<int>(cout," "));

   cout << "} =" << endl << "{";

   set_difference(all.begin(), all.end(),

                  even.begin(), even.end(), odd_ins);

   copy(odd.begin(),odd.end(),

        ostream_iterator<int>(cout," "));

   cout << "}" << endl << endl;

   return 0;

 }


set_intersection

Siehe auch	Algorithmus

Grundlegende Set-Operation für sortierte Sequenzen.

Syntax

#include <algorithm>

template <class InputIterator1, class InputIterator2,

          class OutputIterator>

OutputIterator

set_intersection (InputIterator1 first1, InputIterator1 last1,

                  InputIterator2 first2, InputIterator last2,

                  OutputIterator result);

template <class InputIterator1, class InputIterator2,

          class OutputIterator, class Compare>

 OutputIterator

set_intersection (InputIterator1 first1, InputIterator1 last1,

                  InputIterator2 first2, InputIterator2 last2,

                  OutputIterator result, Compare comp);

Beschreibung

Der Algorithmus set_intersection konstruiert eine sortierte Schnittmenge der Elemente aus den beiden Bereichen. Er liefert das Ende des konstruierten Bereichs zurück. Wenn der ein Element findet, das in beiden Bereichen vorhanden ist, kopiert set_intersection immer das Element aus dem ersten Bereich in result. Auf diese Weise ist das Ergebnis von set_intersection unter allen Umständen stabil. Das Ergebnis von set_intersection ist nicht definiert, wenn es Überschneidungen des Ergebnisbereichs mit einem der Originalbereiche gibt.

set_intersection geht davon aus, daß die Bereiche unter Verwendung des voreingestellten Vergleichsoperators kleiner als (<) sortiert wurden, es sei denn, es wurde ein alternativer Vergleichsoperator (comp) angegeben.

Es werden höchstens ((last1 - first1) + (last2 - first2)) * 2 -1 Vergleiche ausgeführt.

Beispiel

 #include<algorithm>

 #include<set>

 using namespace std;

 int main()

 {

   //Einige Mengen initialisieren

   int a1[10] = {1,3,5,7,9,11};

   int a3[4]  = {3,5,7,8};

   set<int, less<int> > odd(a1, a1+6),

                        result, small(a3,a3+4);

   //Einen insert_iterator für das Ergebnis erzeugen

   insert_iterator<set<int, less<int> > >

                 res_ins(result, result.begin());

   // set_intersection demonstrieren

   cout << "The result of:" << endl << "{";

   copy(small.begin(),small.end(),

        ostream_iterator<int>(cout," "));

   cout << "} intersection {";

   copy(odd.begin(),odd.end(),

        ostream_iterator<int>(cout," "));

   cout << "} =" << endl << "{";

   set_intersection(small.begin(), small.end(),

                   odd.begin(), odd.end(), res_ins);

   copy(result.begin(),result.end(),

        ostream_iterator<int>(cout," "));

   cout << "}" << endl << endl;

   return 0;

 }


set_symmetric_difference

Siehe auch	Algorithmus

Grundlegende Set-Operation für sortierte Sequenzen.

Syntax

#include <algorithm>

template <class InputIterator1, class InputIterator2,

          class OutputIterator>

OutputIterator

set_symmetric_difference (InputIterator1 first1,

                          InputIterator1 last1,

                          InputIterator2 first2,

                          InputIterator2 last2,

                          OutputIterator result);

template <class InputIterator1, class InputIterator2,

          class OutputIterator, class Compare>

OutputIterator

set_symmetric_difference (InputIterator1 first1,

                          InputIterator1 last1,

                          InputIterator2 first2,

                          InputIterator2 last2,

                          OutputIterator result, Compare comp);

Beschreibung

set_symmetric_difference konstruiert eine sortierte symmetrische Differenzmenge der Elemente aus den beiden Bereichen. Das heißt, daß der konstruierte Bereich Kopien der Elemente enthält, die in dem Bereich [first1, last1) vorhanden sind, nicht jedoch in dem Bereich [first2, last2), und Kopien der Elemente enthält, die in dem Bereich [first2, last2) enthalten sind, aber nicht in dem Bereich [first1, last1). Der Algorithmus liefert das Ende des konstruierten Bereichs zurück.

Es soll beispielsweise von diesen beiden Sets ausgegangen werden:

1 2 3 4 5

und

3 4 5 6 7

Die set_symmetric_difference dieser beiden Sets ist:

1 2 6 7

Das Ergebnis von set_symmetric_difference ist nicht definiert, wenn der Ergebnisbereich Überschneidungen mit einem der beiden Originalbereiche aufweist.

set_symmetric_difference geht davon aus, daß die Bereiche unter Verwendung des voreingestellten Vergleichsoperators kleiner als (<) sortiert sind, es sei denn, es wurde ein alternativer Vergleichsoperator (comp) angegeben.

Verwenden Sie den Algorithmus set_ difference, wenn ein Ergebnis zurückgeliefert werden soll, das die Elemente enthält, die in dem ersten Set enthalten sind, aber nicht in dem zweiten.

Es werden höchstens ((last1 - first1) + (last2 - first2)) * 2 -1 Vergleiche ausgeführt.

Beispiel

 #include<algorithm>

 #include<set>

 using namespace std;

 int main()

 {

   //Einige Mengen initialisieren

   int a1[] = {1,3,5,7,9,11};

   int a3[] = {3,5,7,8};

   set<int, less<int> > odd(a1,a1+6), result,

                        small(a3,a3+4);

   //Einen insert_iterator für das Ergebnis erzeugen

   insert_iterator<set<int, less<int> > >

                 res_ins(result, result.begin());

   // set_symmetric_difference demonstrieren

   cout << "The symmetric difference of:" << endl << "{";

   copy(small.begin(),small.end(),

        ostream_iterator<int>(cout," "));

   cout << "} with {";

   copy(odd.begin(),odd.end(),

        ostream_iterator<int>(cout," "));

   cout << "} =" << endl << "{";

   set_symmetric_difference(small.begin(), small.end(),

                       odd.begin(), odd.end(), res_ins);

   copy(result.begin(),result.end(),

        ostream_iterator<int>(cout," "));

   cout << "}" << endl << endl;

   return 0;

 }


set_union

Siehe auch	Algorithmus

Grundlegende Set-Operation für sortierte Sequenzen.

Syntax

#include <algorithm>

template <class InputIterator1, class InputIterator2, class OutputIterator>

OutputIterator

set_union (InputIterator1 first1, InputIterator1 last1,

           InputIterator2 first2, InputIterator2 last2,

           OutputIterator result);

template <class InputIterator1, class InputIterator2,

          class OutputIterator, class Compare>

OutputIterator

set_union (InputIterator1 first1, InputIterator1 last1,

           InputIterator2 first2, InputIterator2 last2,

           OutputIterator result, Compare comp);

Beschreibung

Der Algorithmus set_union konstruiert eine sortierte Vereinigungsmenge der Elemente aus den beiden Bereichen. Er liefert das Ende des konstruierten Bereichs zurück. set_union ist stabil, d.h., wenn ein Element in beiden Bereichen vorhanden ist, wird das Element aus dem ersten Bereich kopiert. Das Ergebnis von set_union ist nicht definiert, wenn es Überschneidungen des Ergebnisbereichs mit einem der Originalbereiche gibt. Beachten Sie, daß set_union die beiden sortierten Sequenzen nicht zusammenführt. Wenn ein Element in beiden Bereichen auftritt, wird nur das Element aus dem ersten Bereich in result kopiert. (Verwenden Sie den Algorithmus merge, wenn Sie eine geordnete Zusammenführung von zwei Sequenzen erhalten möchten. Die Ergebnismenge enthält dann alle Elemente aus beiden Sequenzen.)

set_union geht davon aus, daß die Sequenzen unter Verwendung des voreingestellten Vergleichsoperators kleiner als (<) sortiert sind, es sei denn, es wurde ein alternativer Vergleichsoperator (comp) angegeben.

Es werden höchstens ((last1 - first1) + (last2 - first2)) * 2 -1 Vergleiche ausgeführt.

Beispiel

 #include<algorithm>

 #include<set>

 using namespace std;

 int main()

 {

   //Einige Mengen initialisieren

   int a2[6]  = {2,4,6,8,10,12};

   int a3[4]  = {3,5,7,8};

   set<int, less<int> >  even(a2, a2+6),

                        result, small(a3,a3+4);

   //Einen insert_iterator für das Ergebnis erzeugen

   insert_iterator<set<int, less<int> > >

                 res_ins(result, result.begin());

   // set_union demonstrieren

   cout << "The result of:" << endl << "{";

   copy(small.begin(),small.end(),

        ostream_iterator<int>(cout," "));

   cout << "} union {";

   copy(even.begin(),even.end(),

        ostream_iterator<int>(cout," "));

   cout << "} =" << endl << "{";

   set_union(small.begin(), small.end(),

            even.begin(), even.end(), res_ins);

   copy(result.begin(),result.end(),

        ostream_iterator<int>(cout," "));

   cout << "}" << endl << endl;

   return 0;

 }


sort

Siehe auch	Algorithmus

Algorithmen-Template für das Sortieren von Kollektionen.

Syntax

#include <algorithm>

template <class RandomAccessIterator>

 void sort (RandomAccessIterator first,

            RandomAccessIterator last);

template <class RandomAccessIterator, class Compare>

 void sort (RandomAccessIterator first,

            RandomAccessIterator last, Compare comp);

Beschreibung

Der Algorithmus sort sortiert die Elemente im Bereich [first, last) und verwendet dazu entweder den Operator kleiner als (<) oder den Vergleichsoperator comp. Wenn das Verhalten unter ungünstigsten Umständen (worst case) wichtig ist, sollten die Algorithmen stable_sort oder partial_sort verwendet werden.

sort führt näherungsweise NlogN, wobei N gleich last - first ist, Vergleiche im Schnitt aus.

Beispiel

 #include <vector>

 #include <algorithm>

 #include <functional>

 using namespace std;

 struct associate

 {

   int num;

   char chr;

   associate(int n, char c) : num(n), chr(c){};

   associate() : num(0), chr('\0'){};

 };

 bool operator<(const associate &x, const associate &y)

 {

   return x.num < y.num;

 }

 ostream& operator<<(ostream &s, const associate &x)

 {

   return s << "<" << x.num << ";" << x.chr << ">";

 }

 int main ()

 {

   vector<associate>::iterator i, j, k;

   associate arr[20] =

        {associate(-4, ' '), associate(16, ' '),

         associate(17, ' '), associate(-3, 's'),

         associate(14, ' '), associate(-6, ' '),

         associate(-1, ' '), associate(-3, 't'),

         associate(23, ' '), associate(-3, 'a'),

         associate(-2, ' '), associate(-7, ' '),

         associate(-3, 'b'), associate(-8, ' '),

         associate(11, ' '), associate(-3, 'l'),

         associate(15, ' '), associate(-5, ' '),

         associate(-3, 'e'), associate(15, ' ')};

   // Vektoren einrichten

   vector<associate> v(arr, arr+20), v1((size_t)20),

                 v2((size_t)20);

   // Originalvektor in die Vektoren #1 und #2 kopieren

   copy(v.begin(), v.end(), v1.begin());

   copy(v.begin(), v.end(), v2.begin());

   // Vektor #1 sortieren

   sort(v1.begin(), v1.end());

   // Vektor #2 stabil sortieren

   stable_sort(v2.begin(), v2.end());

   // Ergebnis anzeigen

   cout << "Original    sort      stable_sort" << endl;

   for(i = v.begin(), j = v1.begin(), k = v2.begin();

  	 i != v.end(); i++, j++, k++)

   cout << *i << "     " << *j << "     " << *k << endl;

   return 0;

 }


sort_heap

Algorithmus

Wandelt einen Heap in eine sortierte Kollektion um.

Syntax

#include <algorithm>

template <class RandomAccessIterator>

  void

  sort_heap(RandomAccessIterator first,

            RandomAccessIterator last);

template <class RandomAccessIterator, class Compare>

  void

  sort_heap(RandomAccessIterator first,

            RandomAccessIterator last, Compare comp);

Beschreibung

Ein Heap ist eine bestimmte Organisationsweise für Elemente in einem Bereich zwischen zwei Random Access-Iteratoren [a, b). Seine zwei Hauptmerkmale sind:

1.	*a ist das größte Element im Bereich.

2.	*a kann mittels pop_heap() entfernt werden, oder ein neues Element kann durch push_heap() hinzugefügt werden. Dieser Vorgang beansprucht die Zeit O(logN).

Diese Eigenschaften machen Heaps als Ereigniswarteschlangen verwendbar.

Der Algorithmus sort_heap wandelt einen Heap in eine sortierte Kollektion in dem Bereich [first, last)um. Dazu wird entweder der Standardoperator (<) oder die zusammen mit dem Algorithmus angegebene Vergleichsfunktion verwendet. Beachten Sie, daß sort_heap nicht stabil ist, das heißt, die Elemente stehen nach der Anwendung von sort_heap eventuell nicht in der gleichen Reihenfolge.

sort_heap führt höchstens NlogN Vergleiche aus; dabei ist N gleich last - first.

Beispiel

 #include <algorithm>

 #include <vector>

 using namespace std;

 int main(void)

 {

   int d1[4] = {1,2,3,4};

   int d2[4] = {1,3,2,4};

   // Zwei Vektoren einrichten

   vector<int> v1(d1,d1 + 4), v2(d2,d2 + 4);

   // Heaps anlegen

   make_heap(v1.begin(),v1.end());

   make_heap(v2.begin(),v2.end(),less<int>());

   // v1 = (4,x,y,z)  und  v2 = (4,x,y,z)

   // Beachten Sie, daß x, y und z die verbleibenden Werte

   // im Container (also ungleich 4) darstellen.

   // Die Definition des Heaps und der Heap-Operationen

   // macht keine besondere Anordnung dieser

   // Werte erforderlich.

   // Beide Vektoren nach cout kopieren

   ostream_iterator<int> out(cout," ");

   copy(v1.begin(),v1.end(),out);

   cout << endl;

   copy(v2.begin(),v2.end(),out);

   cout << endl;

   // Jetzt entfernen (pop)

   pop_heap(v1.begin(),v1.end());

   pop_heap(v2.begin(),v2.end(),less<int>());

   // v1 = (3,x,y,4) and v2 = (3,x,y,4)

   // Beide Vektoren nach cout kopieren

   copy(v1.begin(),v1.end(),out);

   cout << endl;

   copy(v2.begin(),v2.end(),out);

   cout << endl;

   // Und einfügen (push)

   push_heap(v1.begin(),v1.end());

   push_heap(v2.begin(),v2.end(),less<int>());

   // v1 = (4,x,y,z) und v2 = (4,x,y,z)

   // Beide Vektoren nach cout kopieren

   copy(v1.begin(),v1.end(),out);

   cout << endl;

   copy(v2.begin(),v2.end(),out);

   cout << endl;

   // Jetzt die Heaps sortieren

   sort_heap(v1.begin(),v1.end());

   sort_heap(v2.begin(),v2.end(),less<int>());

   // v1 = v2 = (1,2,3,4)

   // Beide Vektoren nach cout kopieren

   copy(v1.begin(),v1.end(),out);

   cout << endl;

   copy(v2.begin(),v2.end(),out);

   cout << endl;

   return 0;

 }


stable_partition

Siehe auch	Algorithmus

Plaziert alle Elemente, die dem angegebenen Prädikat entsprechen, vor allen Elementen, die dem Prädikat nicht entsprechen, und behält dabei die relative Ordnung der Elemente in jeder der beiden Gruppen bei.

Syntax

#include <algorithm>

template <class BidirectionalIterator, class Predicate>

BidirectionalIterator

stable_partition (BidirectionalIterator first,

                  BidirectionalIterator last,

                  Predicate pred);

Beschreibung

Der Algorithmus stable_partition plaziert alle Elemente in dem Bereich [first, last), die pred genügen, vor all denjenigen Elementen, die diesem nicht genügen. Er liefert einen Iterator i zurück, der auf das erste Element nach dem Ende der Gruppe von Elementen zeigt, die pred erfüllen. Anders ausgedrückt, gibt stable_partition i so zurück, daß für jeden Iterator j in dem Bereich [first, i), pred(*j) == true ist, und für jeden Iterator k in dem Bereich [i, last), pred(*j) == false. Die relative Ordnung der Elemente in beiden Gruppen bleibt dabei erhalten.

Der Algorithmus partition kann verwendet werden, wenn die relative Ordnung der Elemente innerhalb der Gruppen, die dem Prädikat entsprechen bzw. nicht entsprechen, nicht unbedingt beibehalten werden muß.

Der Algorithmus stable_partition führt höchstens (last - first) * log(last - first) Vertauschungen durch. Dabei wendet er das Prädikat exakt last - first mal an.

Beispiel

#include<functional>

 #include<deque>

 #include<algorithm>

 using namespace std;

 //Erzeugen eines neuen Prädikats aus unary_function

 template<class Arg>

 class is_even : public unary_function<Arg, bool>

 {

   public:

   bool operator()(const Arg& arg1)

   {

      return (arg1 % 2) == 0;

   }

 };

 int main()

 {

   //Initialisieren eines Deques mit einem Integer-Array

   int init[10] = {1,2,3,4,5,6,7,8,9,10};

   deque<int> d(init, init+10);

   //Ausgeben der Originalwerte

   cout << "Unpartitioned values: " << endl << "     ";

   copy(d.begin(),d.end(),ostream_iterator<int>(cout," "));

   cout << endl << endl;

   //Aufteilen des Deques nach dem Kriterium gerade/ungerade

   stable_partition(d.begin(), d.end(), is_even<int>());

   //Ergebnis der Aufteilung ausgeben

   cout << "Partitioned values: " << endl << "     ";

   copy(d.begin(),d.end(),ostream_iterator<int>(cout," "));

   return 0;

 }


stable_sort

Siehe auch	Algorithmus

Algorithmen-Template für das Sortieren von Kollektionen.

Syntax

#include <algorithm>

template <class RandomAccessIterator>

 void stable_sort (RandomAccessIterator first,

                   RandomAccessIterator last);

template <class RandomAccessIterator, class Compare>

 void stable_sort (RandomAccessIterator first,

                   RandomAccessIterator last,

                   Compare comp);

Beschreibung

Der Algorithmus stable_sort sortiert die Elemente im Bereich [first, last). Die erste Fassung des Algorithmus verwendet den Operator kleiner als (<) als Vergleichsoperator für die Sortierung. Die zweite Fassung verwendet die Vergleichsfunktion comp.

Der Algorithmus stable_sort wird als stabil angesehen, weil die relative Ordnung gleicher Elemente erhalten bleibt.

stable_sort führt höchstens N(logN) **2 Vergleiche durch, wobei N gleich last -first ist. Wenn genügend zusätzlicher Speicher zur Verfügung steht, gilt NlogN.

Beispiel

 #include <vector>

 #include <algorithm>

 #include <functional>

 using namespace std;

 struct associate

 {

   int num;

   char chr;

   associate(int n, char c) : num(n), chr(c){};

   associate() : num(0), chr('\0'){};

 };

 bool operator<(const associate &x, const associate &y)

 {

   return x.num < y.num;

 }

 ostream& operator<<(ostream &s, const associate &x)

 {

   return s << "<" << x.num << ";" << x.chr << ">";

 }

 int main ()

 {

   vector<associate>::iterator i, j, k;

   associate arr[20] =

        {associate(-4, ' '), associate(16, ' '),

         associate(17, ' '), associate(-3, 's'),

         associate(14, ' '), associate(-6, ' '),

         associate(-1, ' '), associate(-3, 't'),

         associate(23, ' '), associate(-3, 'a'),

         associate(-2, ' '), associate(-7, ' '),

         associate(-3, 'b'), associate(-8, ' '),

         associate(11, ' '), associate(-3, 'l'),

         associate(15, ' '), associate(-5, ' '),

         associate(-3, 'e'), associate(15, ' ')};

   // Vektoren einrichten

   vector<associate> v(arr, arr+20), v1((size_t)20),

                 v2((size_t)20);

   // Ursprünglichen Vektor in die Vektoren #1 und #2 kopieren

   copy(v.begin(), v.end(), v1.begin());

   copy(v.begin(), v.end(), v2.begin());

   // Vektor #1 sortieren

   sort(v1.begin(), v1.end());

   // Vektor #2 stabil sortieren

   stable_sort(v2.begin(), v2.end());

   // Ergebnis anzeigen

   cout << "Original    sort      stable_sort" << endl;

   for(i = v.begin(), j = v1.begin(), k = v2.begin();

  	 i != v.end(); i++, j++, k++)

   cout << *i << "     " << *j << "     " << *k << endl;

   return 0;

 }


stack

Siehe auch	Container

Ein Container-Adapter, der sich wie ein Stack verhält (last in, first out).

Syntax

#include <stack>

template <class T, class Container = deque<T>>

 class stack {

public:

// Typdefinitionen

   typedef Container::value_type value_type;

   typedef Container::size_type size_type;

protected:

   Container c;

public:

// Elementzugriff

   bool empty () const;

   size_type size () const;

   value_type& top ();

   const value_type& top () const;

   void push (const value_type& x);

   void pop ();

};

template <class T, class Container = deque<T>>

 bool   operator==   (const   stack<Container>&   x,   const

 stack<Container>& y);

template <class T, class Container = deque<T>>

 bool   operator<   (const   stack<Container>&   x,    const

 stack<Container>& y);

Beschreibung

Der Container-Adapter stack bewirkt, daß sich ein Container wie ein Stack  des Typs "last in, first out" (LIFO) verhält. Das letzte Element, das auf dem Stack abgelegt ("pushed") wurde, ist das erste Element, das wieder entfernt wird ("popped" off). Der Stack kann jeden Container adaptieren, der diese Operationen aufweist: back(), push_back() und pop_back(). Insbesondere können vector , list  und deque verwendet werden.

Beschränkungen

Wenn der verwendete Compiler voreingestellte Template-Parameter nicht unterstützt, muß ein Template-Parameter für Container angegeben werden. Ein Beispiel:

Die folgende Schreibweise ist fehlerhaft:

stack<int> var;

Statt dessen ist zu schreiben:

stack<int, deque<int>> var;

Beispiel

 #include <stack>

 #include <vector>

 #include <deque>

 #include <string>

 using namespace std;

 int main(void)

 {

   // Einen Stack anlegen und dazu einen Vektor-Container verwenden

   stack<int,vector<int> > s;

   // Zwei Werte auf dem Stack ablegen

   s.push(1);

   s.push(2);

   cout << s.top() << endl;

   // Die Werte nun wieder entfernen

   s.pop();

   cout << s.top() << endl;

   s.pop();

   // Einen Stack von Strings anlegen und dazu einen Deque verwenden

   stack<string,deque<string> > ss;

   // Einige Strings ablegen und dann wieder entfernen

   int i;

   for (i = 0; i < 10; i++)

   {

     ss.push(string(i+1,'a'));

     cout << ss.top() << endl;

   }

   for (i = 0; i < 10; i++)

   {

     cout << ss.top() << endl;

     ss.pop();

   }

   return 0;

 }

Elementfunktionen

bool

empty () const;

Gibt true zurück, wenn der Stack leer ist, andernfalls false.

void

pop ();

Entfernt das oberste Element vom Stack.

void

push (const value_type& x);

Legt x auf dem Stack ab.

size_type

size () const;

Liefert die Anzahl der Elemente auf dem Stack zurück.

value_type&

top ();

Liefert das oberste Element im Stack zurück. Dies ist das zuletzt auf dem Stack abgelegte Element, wenn nicht seit diesem Zeitpunkt pop() aufgerufen wurde.

const value_type&

top () const;

Liefert das oberste Element auf dem Stack als const value_type zurück.


Stream-Iteratoren

Siehe auch	Iteratoren

Stream-Iteratoren stellen Iteratoreneigenschaften für Eingabe- (istreams) und Ausgabe-Streams (ostreams) zur Verfügung. Sie ermöglichen die direkte Anwendung von generischen Algorithmen auf Streams.


string

Siehe auch	String-Bibliothek

Eine Spezialisierung der Klasse basic_string.


swap

Siehe auch	Algorithmus

Werte, die an zwei Positionen gespeichert sind, gegeneinander austauschen.

Syntax

#include <algorithm>

template <class T>

 void swap (T& a, T& b);

Beschreibung

Der Algorithmus swap vertauscht die Werte in a und b.


swap_ranges

Siehe auch	Algorithmus

Vertauscht einen Bereich von Werten an einer Position mit den Werten an einer anderen Position.

Syntax

#include <algorithm>

template <class ForwardIterator1, class ForwardIterator2>

 ForwardIterator2 swap_ranges (ForwardIterator1 first1,

                              ForwardIterator last1,

                              ForwardIterator2 first2);

Beschreibung

Der Algorithmus swap_ranges vertauscht korrespondierende Werte in zwei Bereichen auf folgende Weise.

Für jeden nicht negativen Integer n < (last - first) tauscht die Funktion *(first1 + n) gegen *(first2 + n)) aus. Nach der Durchführung aller Vertauschungen gibt swap_ranges einen Iterator zurück, der auf das Ende des zweiten Containers zeigt, d.h. first2 + (last1 -first1). Das Ergebnis von swap_ranges ist nicht definiert, wenn sich die beiden Bereiche [first, last) und [first2, first2 + (last1 - first1)) überschneiden.

Beispiel

 #include <vector>

 #include <algorithm>

 using namespace std;

 int main()

 {

   int d1[] = {6, 7, 8, 9, 10, 1, 2, 3, 4, 5};

   // Einen Vektor einrichten

   vector<int> v(d1,d1 + 10);

   // Ursprünglichen Vektor ausgeben

   cout << "For the vector: ";

   copy(v.begin(),v.end(),ostream_iterator<int>(cout," "));

   // Die ersten fünf Elemente gegen die letzten fünf Elemente austauschen

   swap_ranges(v.begin(),v.begin()+5, v.begin()+5);

   // Ergebnis ausgeben

   cout << endl << endl

        << "Swapping the first five elements "

        << "with the last five gives: "

        << endl << "     ";

   copy(v.begin(),v.end(),ostream_iterator<int>(cout," "));

   return 0;

 }


times

Funktionsobjekt

Ein binäres Funktionsobjekt, das das Ergebnis der Multiplikation seines ersten Arguments mit seinem zweiten Argument zurückliefert.

Syntax

#include<functional>

   template <class T>

   struct times : binary_function<T, T, T> {

        T operator() (const T& x, const T& y) const

              { return x * y; }

   };

Beschreibung

times ist ein binäres Funktionsobjekt. Sein operator() gibt das Ergebnis der Multiplikation von x und y zurück. Ein Objekt times kann an jeden Algorithmus übergeben werden, der eine binäre Funktion verwendet. Beispielsweise wendet der Algorithmus transform eine binäre Operation auf korrespondierende Werte in zwei Kollektionen an und speichert das Ergebnis. times würde in diesem Algorithmus in folgender Weise verwendet:

vector<int> vec1;

vector<int> vec2;

vector<int> vecResult;

.

.

.

transform(vec1.begin(), vec1.end(),
          vec2.begin(), vec2.end(),
          vecResult.begin(), times<int>());

Nach diesem Aufruf von transform enthält vecResult(n) vec1(n) mal vec2(n).


transform

Algorithmus

Wendet eine Operation auf einen Bereich von Werten in einer Kollektion an und speichert das Ergebnis.

Syntax

#include <algorithm>

template <class InputIterator,

          class OutputIterator,

          class UnaryOperation>

OutputIterator transform (InputIterator first,

                          InputIterator last,

                          OutputIterator result,

                          UnaryOperation op);

template <class InputIterator1,

          class InputIterator2,

          class OutputIterator,

          class BinaryOperation>

OutputIterator transform (InputIterator1 first1,

                          InputIterator1 last1,

                          InputIterator2 first2,

                          OutputIterator result,

                          BinaryOperation binary_op);

Beschreibung

Es gibt zwei Formen des Algorithmus transform. Die erste Form wendet die unäre Operation op auf jedes Element in dem Bereich [first,last) an und sendet das Ergebnis an den Ausgabeiterator result. Diese Version von transform könnte zum Beispiel verwendet werden, um jedes Element in einem Vektor zum Quadrat zu erheben. Wenn der Ausgabeiterator (result) zugleich auch der Eingabeiterator ist, der zum Durchgang durch den Bereich verwendet wird, führt transform die Umwandlungen stationär (inplace) durch.

Die zweite Form von transform wendet eine binäre Operation binary_op auf korrespondierende Elemente in dem Bereich [first1, last1) und dem Bereich, der bei first2 beginnt, an und sendet das Ergebnis an result. transform kann zum Beispiel verwendet werden, um korrespondierende Elemente in zwei Sequenzen zu addieren und die Menge der Summen in einer dritten Sequenz zu speichern. Der Algorithmus geht, ohne sie zu prüfen, von der Annahme aus, daß die zweite Sequenz wenigstens so viele Elemente enthält wie die erste. Beachten Sie, daß der Ausgabeiterator result eine dritte Sequenz sein kann oder eine der beiden Eingabesequenzen.

Formal weist transform durch jeden Iterator i in dem Bereich [result, result + (last1 - first1)) einen neuen korrespondierenden Wert zu, der gleich:

op(*(first1 + (i - result))

oder

binary_op(*(first1 + (i - result), *(first2 + (i - result)))

ist. transform gibt result + (last1 - first1) zurück. op und binary_op dürfen keine Nebeneffekte haben. result kann im Falle eine unären Transformation gleich first oder im Falle einer binären Transformation gleich first1 oder first2 sein.

Es werden exakt last1 - first1 Anwendungen von op oder binary_op ausgeführt.

Beispiel

 #include<functional>

 #include<deque>

 #include<algorithm>

 #include<iomanip.h>

 using namespace std;

 int main()

 {

   //Einen Deque mit einem Integer-Array initialisieren

   int arr1[5] = {99, 264, 126, 330, 132};

   int arr2[5] = {280, 105, 220, 84, 210};

   deque<int> d1(arr1, arr1+5), d2(arr2, arr2+5);

   //Originalwerte ausgeben

   cout << "The following pairs of numbers: "

        << endl << "     ";

   deque<int>::iterator i1;

   for(i1 = d1.begin(); i1 != d1.end(); i1++)

      cout << setw(6) << *i1 << " ";

   cout << endl << "     ";

   for(i1 = d2.begin(); i1 != d2.end(); i1++)

      cout << setw(6) << *i1 << " ";

   // Die Zahlen im Deque in ihre Faktoren umwandeln

   // und sie im Vektor speichern

   transform(d1.begin(), d1.end(), d2.begin(),

             d1.begin(), times<int>());

   //Ergebnisse anzeigen

   cout << endl << endl;

   cout << "Have the products: " << endl << "     ";

   for(i1 = d1.begin(); i1 != d1.end(); i1++)

     cout << setw(6) << *i1 << " ";

   return 0;

 }


unary_function

Funktionsobjekt

Abstrakte Basisfunktion für unäre Funktionsobjekte.

Syntax

#include <functional>

    template <class Arg, class Result>

    struct unary_function{

         typedef Arg argument_type;

         typedef Result result_type;

    };

Beschreibung

Funktionsobjekte sind Objekte, für die ein operator() definiert ist. Sie sind wichtig für die effiziente Verwendung der generischen Algorithmen der Standardbibliothek, da die Schnittstelle für jedes Algorithmen-Template entweder ein Objekt mit einem definierten operator() annehmen kann oder einen Zeiger auf eine Funktion. Die Standardbibliothek stellt sowohl eine Standardmenge von Funktionsobjekten zur Verfügung als auch zwei Klassen, die als Basis für die Erzeugung eigener Funktionsobjekte verwendet werden können.

Funktionsobjekte, die ein Argument annehmen, heißen unäre Funktionsobjekte. Unäre Funktionsobjekte müssen die Typdefinitionen argument_type und result_type zur Verfügung stellen. Die Klasse unary_function erleichtert die Erzeugung von Templates für unäre Funktionsobjekte, indem sie die erforderlichen Typdefinitionen für ein unäres Funktionsobjekt zur Verfügung stellt. Durch Ableiten aus unary_function können eigene unäre Funktionsobjekte erzeugt werden.


unary_negate

Siehe auch	Funktionsadapter (Negator)

Funktionsobjekt, das das Komplement des Ergebnisses seines unären Prädikats zurückgibt.

Syntax

#include<functional>

template <class Predicate>

class unary_negate

  : public unary_function<Predicate::argument_type, bool> {

public:

   explicit unary_negate (const Predicate& pred);

   bool operator() (const argument_type& x) const;

};

template<class Predicate>

unary_negate <Predicate> not1 (const Predicate& pred);

Beschreibung

unary_negate ist eine Klasse von Funktionsobjekten, die einen Rückgabetyp für den Funktionsadapter not1 zur Verfügung stellt. not1 ist ein Funktionsadapter, ein sogenannter Negator, der ein unäres Prädikat-Funktionsobjekt als Argument annimmt und ein unäres Prädikat-Funktionsobjekt zurückliefert, das das Komplement des Originals ist.

Beachten Sie, daß not1 nur mit Funktionsobjekten verwendet werden kann, die als Unterklassen der Klasse unary_function definiert sind.

Konstruktor

explicit unary_negate (const Predicate& pred);

Konstruiert ein Objekt des Typs unary_negate aus dem Prädikat pred.

Operator

bool operator() (const argument_type& x) const;

Liefert das Ergebnis von pred(x) zurück.


uninitialized_copy

Siehe auch	Speicherverwaltung

Ein Algorithmus, der construct verwendet, um Werte aus einem Bereich an eine andere Position zu kopieren.

Syntax

#include <memory>

template <class InputIterator, class ForwardIterator>

ForwardIterator uninitialized_copy (InputIterator first,

                                    InputIterator last,

                                    ForwardIterator result);

Beschreibung

uninitialized_copy kopiert alle Elemente im Bereich [first, last) an die Position, die bei result beginnt. Dazu wird der Algorithmus construct verwendet.


uninitialized_fill

Siehe auch	Speicherverwaltung

Ein Algorithmus, der den Algorithmus construct verwendet, um Werte in einer Kollektion zu setzen.

Syntax

#include <memory>

template <class ForwardIterator, class T>

void uninitialized_fill(ForwardIterator first,

                        ForwardIterator last,

                        const T& x);

Beschreibung

uninitialized_fill initialisiert alle Elemente in dem Bereich [first,last) mit dem Wert x und verwendet dazu den Algorithmus construct.


uninitialized_fill_n

Siehe auch	Speicherverwaltung

Algorithmus, der den Algorithmus construct verwendet, um Werte in einer Kollektion zu setzen.

Syntax

#include <memory>

template <class ForwardIterator,

          class Size, class T>

void uninitialized_fill_n (ForwardIterator first,

                           Size n, const T& x);

Beschreibung

unitialized_fill_n beginnt bei dem Iterator first und initialisiert die ersten n Elemente mit dem Wert x. Dazu wird der Algorithmus construct verwendet.


unique, unique_copy

Algorithmus

Entfernt aufeinanderfolgende Duplikate aus einem Wertebereich und plaziert die resultierenden eindeutigen Werte in der Ergebnismenge.

Syntax

#include <algorithm>

template <class ForwardIterator>

ForwardIterator unique (ForwardIterator first,

                        ForwardIterator last);

template <class ForwardIterator, class BinaryPredicate>

ForwardIterator unique (ForwardIterator first,

                        ForwardIterator last,

                        BinaryPredicate binary_pred);

template <class InputIterator, class OutputIterator>

OutputIterator unique_copy (InputIterator first,

                            InputIterator last,

                            OutputIterator result);

template <class InputIterator,

          class OutputIterator,

          class BinaryPredicate>

OutputIterator unique_copy (InputIterator first,

                            InputIterator last,

                            OutputIterator result,

                            BinaryPredicate binary_pred);

Beschreibung

Der Algorithmus unique bewegt sich durch eine Sequenz und entfernt alle Elemente mit Ausnahme des ersten aus jeder zusammenhängenden Gruppe gleicher Elemente. Es gibt zwei Fassungen dieses Algorithmus. Eine prüft auf Gleichheit, und die zweite prüft, ob ein binäres Prädikat erfüllt ist, wenn es auf benachbarte Elemente angewandt wird. Ein Element ist dann eindeutig, wenn es der hier aufgeführten Bedingung nicht entspricht:

  *i  ==  *(i  -  1)

oder

binary_pred(*i, *(i - 1)) == true.

Wenn ein Element eindeutig ist, wird es an den Anfang der Sequenz kopiert; vorhandene Elemente werden dabei überschrieben. Sobald alle eindeutigen Elemente identifiziert wurden, bleibt der Rest der Sequenz unverändert, und unique liefert das Ende des resultierenden Bereichs zurück.

Der Algorithmus unique_copy kopiert das erste Element jeder zusammenhängenden Gruppe gleicher Elemente in einen Ausgabeiterator. Der Algorithmus unique_copy existiert ebenfalls in zwei Versionen. Eine prüft auf Gleichheit, und die andere prüft die benachbarten Elemente auf Erfüllung eines binären Prädikats.

unique_copy liefert das Ende des sich ergebenden Bereichs zurück.

Es werden exakt (last - first) - 1 Anwendungen des korrespondierenden Prädikats ausgeführt.

Beispiel

 #include<algorithm>

 #include<vector>

 using namespace std;

 int main()

 {

   //Initialisieren von zwei Vektoren

   int a1[20] = {4, 5, 5, 9, -1, -1, -1, 3, 7, 5,

                 5, 5, 6, 7, 7, 7, 4, 2, 1, 1};

   vector<int> v(a1, a1+20), result;

   //Erzeugen eines insert_iterator für die Ergebnisse

   insert_iterator<vector<int> > ins(result,

                                  result.begin());

   //Demonstrieren der Textausgabe

   cout << "The vector: " << endl << "    ";

   copy(v.begin(),v.end(),ostream_iterator<int>(cout," "));

   //Suchen der eindeutigen Elemente

   unique_copy(v.begin(), v.end(), ins);

   //Anzeigen der Ergebnisse

   cout << endl << endl

        << "Has the following unique elements:"

        << endl << "     ";

   copy(result.begin(),result.end(),

        ostream_iterator<int>(cout," "));

   return 0;

}


upper_bound

Siehe auch	Algorithmus

Ermittelt die letzte gültige Position für einen Wert in einem sortierten Container.

Syntax

#include <algorithm>

template <class ForwardIterator, class T>

   ForwardIterator

   upper_bound(ForwardIterator first, ForwardIterator last,

                const T& value);

 template <class ForwardIterator, class T, class Compare>

    ForwardIterator

    upper_bound(ForwardIterator first, ForwardIterator last,

                const T& value, Compare comp);

Beschreibung

Der Algorithmus upper_bound gehört zu einer Menge binärer Suchalgorithmen. Alle diese Algorithmen führen binäre Suchoperationen in geordneten Containern durch. Von jedem dieser Algorithmen existieren zwei Fassungen. Die erste verwendet den Operator kleiner als (operator <) für den Vergleich und geht davon aus, daß dieser Operator auch für die Sortierung der Sequenz verwendet wurde. Die zweite Version erlaubt die Aufnahme eines Funktionsobjekts vom Typ compare und geht wiederum davon aus, daß compare die für die Sortierung der Sequenz verwendete Funktion ist. Das Funktionsobjekt muß ein binäres Prädikat sein.

Der Algorithmus upper_bound sucht die letzte Position in einem Container, die value einnehmen kann, ohne die Ordnung des Containers zu verletzen. Der Rückgabewert von upper_bound ist der Iterator für das erste Element in dem Container, das größer als value ist, oder - wenn der Vergleichsoperator verwendet wird - das erste Element, das der Vergleichsfunktion nicht genügt. Da der Algorithmus für die Durchführung der Suche auf die Verwendung des Operators kleiner als oder der benutzerdefinierten Funktion beschränkt ist, liefert upper_bound einen Iterator i in dem Bereich [first, last) zurück, so daß für jeden Iterator j im Bereich [first, i) die entsprechende Version der folgenden Bedingungen erfüllt ist:

!(value < *j)

oder

 comp(value, *j) == false

upper_bound führt höchstens log(last - first) + 1 Vergleiche aus.

Beispiel

 #include <vector>

 #include <algorithm>

 using namespace std;

 int main()

 {

   typedef vector<int>::iterator iterator;

   int d1[11] = {0,1,2,2,3,4,2,2,2,6,7};

   // Einen Vektor einrichten

   vector<int> v1(d1,d1 + 11);

   // Die Varianten von lower_bound ausprobieren

   iterator it1 = lower_bound(v1.begin(),v1.end(),3);

   // it1 = v1.begin() + 4

   iterator it2 =

       lower_bound(v1.begin(),v1.end(),2,less<int>());

   // it2 = v1.begin() + 4

   // Die Varianten von upper_bound ausprobieren

   iterator it3 = upper_bound(v1.begin(),v1.end(),3);

   // it3 = vector + 5

   iterator it4 =

      upper_bound(v1.begin(),v1.end(),2,less<int>());

   // it4 = v1.begin() + 5

   cout << endl << endl

        << "The upper and lower bounds of 3: ( "

        << *it1 << " , " << *it3 << " ]" << endl;

   cout << endl << endl

        << "The upper and lower bounds of 2: ( "

        << *it2 << " , " << *it4 << " ]" << endl;

   return 0;

 }


vector

Siehe auch	Container

Sequenz, die Random Access-Iteratoren unterstützt.

Syntax

#include <vector>

template <class T>

public:

 // Typen

   typedef typename reference;

   typedef typename const_reference;

   typedef typename iterator;

   typedef typename const_iterator;

   typedef typename size_type;

   typedef typename difference_type;

   typedef T value_type;

   typedef  reverse_iterator<iterator, value_type,

            reference, difference_type> reverse_iterator;

   typedef const_reverse_iterator<const_iterator,

           value_type,      reference,      difference_type>

const_reverse_iterator;

 // Konstruieren/Kopieren/Entfernen

   explicit vector ();

   explicit vector (size_type, const T& = T());

   vector (const vector<T>&);

   template <class InputIterator>

    vector (InputIterator, InputIterator);

   ~vector ();

   vector<T>& operator= (const vector<T>&);

   template <class InputIterator>

    void assign (InputIterator first, InputIterator last);

   template <class Size, class T>

    void assign (Size n, const T& t = T());

 // Iteratoren

   iterator begin ();

   const_iterator begin () const;

   iterator end ();

   const_iterator end () const;

   reverse_iterator rbegin ();

   const_reverse_iterator rbegin () const;

   reverse_iterator rend ();

   const_reverse_iterator rend () const;

 // Kapazität

   size_type size () const;

   size_type max_size () const;

   void resize (size_type,T c = T());

   size_type capacity () const;

   bool empty () const;

   void reserve (size_type);

 // Elementzugriff

   reference operator[] (size_type);

   const_reference operator[] (size_type) const;

   reference at (size_type n);

   const_reference at (size_type n) const;

   reference front ();

   const_reference front () const;

   reference back ();

   const_reference back () const;

 // Modifizierer

   void push_back (const T&);

   void pop_back ();

   iterator insert (iterator, const T& = T());

   void insert (iterator, size_type, const T& = T());

   template <class InputIterator>

    void insert (iterator, InputIterator, InputIterator);

   void erase (iterator);

   void erase (iterator, iterator);

   void swap (vector<T>&);

};

 // Vergleich

template <class T>

 bool operator== (const vector<T>&, const vector <T>&);

template <class T>

 bool operator< (const vector<T>&, const vector<T>&);

Beschreibung

vector<T> ist ein Sequenztyp, der Random Access-Iteratoren unterstützt. Darüber hinaus unterstützt er Einfüge- und Löschoperationen am Ende des Containers mit konstanter Ausführungszeit. Einfüge- und Löschoperationen in der Mitte des Containers werden in linearer Zeit ausgeführt. Die Speicherverwaltung geschieht automatisch In vector ist iterator ein Random Access-Iterator, der sich auf T bezieht. const_iterator ist ein konstanter Random Access-Iterator, der sich auf const T bezieht. Es existiert ein Konstruktor für iterator und const_iterator. size_type ist ein vorzeichenloser Integertyp, difference_type ein Integertyp mit Vorzeichen.

Jeder Typ, der für den Template-Parameter T verwendet wird, muß den folgenden Bedingungen genügen (dabei ist T der Typ, t ein Wert (value) von T und u ein const value von T):

  Standardkonstruktor   T()

  Kopierkonstruktoren   T(t) und T(u)

  Destruktor            t.~T()

  Adresse von           &t und &u, was T* beziehungsweise

                        const T* ergibt

  Zuweisung             t = a; dabei ist a ein

                        (unter Umständen const-) Wert von T

Beschränkungen

In allen Containern der Standardbibliothek werden Elementfunktions-Templates verwendet. Ein Beispiel für dieses Merkmal ist der Konstruktor für vector<T>, der zwei Iterator-Templates annimmt:

template <class InputIterator>

 vector (InputIterator, InputIterator);

vector verfügt darüber hinaus über eine Einfügefunktion dieses Typs. Diese Funktionen erlauben - sofern das nicht durch Compiler-Beschränkungen verhindert wird - die Verwendung jedes Eingabeiterator-Typs als Argument. Für Compiler, die dieses Merkmal nicht unterstützen, stehen Ersatzfunktionen zur Verfügung. Diese gestatten die Verwendung eines Iterators, der aus dem gleichen Container-Typ wie dem zu erzeugenden stammt (bzw. demjenigen, für den eine Elementfunktion aufgerufen wird). Alternativ kann ein Zeiger auf den im Container enthaltenen Elementtyp verwendet werden.

Wenn der verwendete Compiler die Verwendung von Templates für Elementfunktionen nicht unterstützt, kann ein Vektor beispielsweise auf eine der beiden folgenden Arten konstruiert werden:

int intarray[10];

vector<int> first_vector(intarray,intarray + 10);

vector<int>

second_vector(first_vector.begin(),first_vector.end());

nicht jedoch in dieser Weise:

vector<long>

long_vector(first_vector.begin(),first_vector.end());

da long_vector und first_vector nicht vom gleichen Typ sind.

Beispiel

 #include <vector>

 using namespace std;

 ostream& operator<<(ostream& out, const vector<int>& v)

 {

   copy(v.begin(), v.end(), ostream_iterator<int>(out," "));

   return out;

 }

 int main(void)

 {

   // einen Vektor mit Doubles und einen mit Integern erzeugen

   vector<int>         vi;

   int                 i;

   for(i = 0; i < 10; ++i) {

     // Werte vor dem Anfang einfügen

     vi.insert(vi.begin(), i);

   }

   // Vektor ausgeben

   cout << vi << endl;

   // jetzt die Hälfte der Elemente löschen

   int      half = vi.size() >> 1;

   for(i = 0; i < half; ++i) {

     vi.erase(vi.begin());

   }

   // noch einmal ausgeben

   cout << vi << endl;

   return 0;

 }

Konstruktoren und Destruktoren

explicit vector ();

Der Standardkonstruktor. Erzeugt einen Vektor mit der Länge null.

explicit vector (size_type n, const T& value = T());

Erzeugt einen Vektor mit der Länge n, der n Exemplare des Werts value enthält.

vector (const vector<T>& x);

Erzeugt eine Kopie von x.

template <class InputIterator>

vector (InputIterator first, InputIterator last);

Erzeugt einen Vektor mit der Länge last - first, der mit allen Werten gefüllt ist, die durch Dereferenzieren der InputIterators im Bereich [first, last) erhalten werden;

~vector ();

Destruktor. Gibt den gesamten für diesen Vektor alloziierten Arbeitsspeicher frei.

Iteratoren

iterator begin ();

Liefert einen Random Access-Iterator zurück, der auf das erste Element zeigt.

const_iterator begin () const;

Liefert einen konstanten Random Access-Iterator zurück, der auf das erste Element zeigt.

iterator end ();

Liefert einen Random Access-Iterator zurück, der auf den Nach-Bereichsende-Wert zeigt.

const_iterator end () const;

Liefert einen konstanten Random Access-Iterator zurück, der auf den Nach-Bereichsende-Wert zeigt.

reverse_iterator rbegin ();

Liefert einen Random Access-Iterator zurück, der auf den Nach-Bereichsende-Wert zeigt.

const_reverse_iterator rbegin () const;

Liefert einen konstanten Random Access-Iterator zurück, der auf den Nach-Bereichsende-Wert zeigt.

reverse_iterator rend ();

Liefert einen Random Access-Iterator zurück, der auf das erste Element zeigt.

const_reverse_iterator rend () const;

Liefert einen konstanten Random Access-Iterator zurück, der auf das erste Element zeigt.

Zuweisungsoperator

vector<T>& operator= (const vector<T>& x);

Zuweisungsoperator. Löscht alle Elemente in self und fügt dann eine Kopie jedes in x enthaltenen Elements in self ein. Liefert eine Referenz auf self zurück.

Referenzoperatoren, Referenzoperator [] (size_type n);

Liefert eine Referenz auf das Element n von self zurück. Das Ergebnis kann als lvalue verwendet werden. Der Index n muß zwischen 0 und size minus eins liegen.

const_reference operator[] (size_type n) const;

Liefert eine konstante Referenz auf das Element n von self zurück. Der Index n muß zwischen 0 und size minus eins liegen.

Elementfunktionen

template <class InputIterator>

void

assign (InputIterator first, InputIterator last);

Löscht alle Elemente, die in self enthalten sind und fügt anschließend neue Elemente aus dem Bereich [first, last) ein.

template <class Size, class T>

void

assign (Size n, const T& t = T());

Löscht alle in self enthaltenen Elemente und fügt dann n Instanzen des Wertes von t ein.

reference

at(size_type n);

Liefert eine Referenz auf das Element n von self zurück. Das Ergebnis kann als lvalue verwendet werden. Der Index n muß zwischen 0 und size minus eins liegen.

const_reference

at (size_type) const;

Liefert eine konstante Referenz auf das Element n von self zurück. Der Index n muß zwischen 0 und size minus eins liegen.

reference

back ();

Liefert eine Referenz auf das letzte Element zurück.

const_reference

back () const;

Liefert eine konstante Referenz auf das letzte Element zurück.

size_type

capacity () const;

Liefert die Größe des zugewiesenen Speichers zurück.

bool

empty () const;

Liefert true zurück, wenn die Größe size null ist.

void

erase (iterator position);

Entfernt das Element, auf das position zeigt.

void

erase (iterator first, iterator last);

Entfernt die Elemente im Bereich [first, last).

reference

front ();

Liefert eine Referenz auf das erste Element zurück.

const_reference

front () const;

Liefert eine konstante Referenz auf das erste Element zurück.

iterator

insert (iterator position, const T& x = T());

Fügt x vor position ein. Der Rückgabewert zeigt auf das eingefügte x.

void

insert (iterator position, size_type n, const  T& x = T());

Fügt n Exemplare von x vor position ein.

template <class InputIterator>

void

insert (iterator position, InputIterator first, InputIterator last);

Fügt Kopien der Elemente in dem Bereich [first, last] vor position ein.

size_type

max_size () const;

Liefert die Größe size() des größten möglichen Vektors zurück.

void

pop_back ();

Entfernt das letzte Element von self .

void

push_back (const T& x);

Fügt ein Exemplar von x am Ende von self ein.

void

reserve (size_type n);

Erhöht die Kapazität von self als Vorbereitung für das Einfügen neuer Elemente. reserve fügt selbst aber keine neuen Elemente hinzu. Nach einem Aufruf von reserve ist capacity() größer als oder gleich n, und anschließende Einfügungen lösen keine Neualloziierung aus, bis die Größe des Vektors den Wert n überschreitet. Es findet keine Neualloziierung statt, wenn der Wert von n kleiner ist als die aktuelle Kapazität capacity(). Wenn eine Neualloziierung vorgenommen wird, werden dadurch alle Iteratoren und Referenzen, die auf Elemente im Vektor zeigen, ungültig. reserve beansprucht im ungünstigsten Fall lineare Ausführungszeit entsprechend der Größe von self .

void

resize (size_type sz, T c = T());

Ändert die Größe von self . Wenn die neue Größe (sz) größer ist als die aktuelle Größe, werden sz-size() cs am Ende des Vektors eingefügt. Wenn die neue Größe kleiner als die aktuelle Kapazität capacity ist, wird der Vektor beschnitten, indem size()-sz Elemente am Ende entfernt werden. Wenn sz gleich capacity ist, wird keine Aktion ausgeführt.

size_type

size () const;

Liefert die Anzahl der Elemente zurück.

void

swap (vector<T>& x);

Tauscht self gegen x aus.

Nicht-Element-Operatoren-Template <class T>

bool operator== (const vector<T>& x, const  vector  <T>& y);

Liefert true zurück, wenn x gleich y ist.

template <class T>

bool operator< (const vector<T>& x,  const  vector  <T>& y);

Liefert true zurück, wenn die in x enthaltenen Elemente lexikalisch niedriger sind als die in y enthaltenen Elemente.


wstring

String-Bibliothek

Eine Spezialisierung der Klasse basic_string. Weitere Informationen über Strings finden Sie unter basic_string.